2021CCPC网络赛第一场 题解

比赛传送门
作者: fn

背景
由于某些众所周知的原因,网络赛重赛了。所以这是网络赛“第一场”。

目录

  • 签到题
    • 1001题 Cut The Wire / 切电线
  • 基本题
    • 1009题 Command Sequence / 指令序列
    • 1006题 Power Sum / 平方和
    • 1002题 Time-division Multiplexing / 时分多路转换


签到题

1001题 Cut The Wire / 切电线

题目大意
有一条只有起点,没有终点,无限长的路。有无数的路灯,编号从1(起点)到无穷。 路灯是用电线连接的,有一条法则:
对于路灯x, 如果x是偶数,则 x x x x 2 \frac{x}{2} 2x 用导线连接; 如果x是奇数,那么 x x x 3 x + 1 3x+1 3x+1 是用导线连接的。

站在路灯 n n n n + 1 n+1 n+1 的中间,切断所有经过的电线。 也就是说,他将切断所有 a ≤ n a≤n an b > n b>n b>n 路灯a和b之间的连接线。计算切断电线的数量。

考察内容
数学

分析
对n左侧的所有x,分奇偶 O ( 1 ) O(1) O(1) 计算。
偶数x,数量为 ⌊ n + 1 2 ⌋ \lfloor \frac{n+1}{2} \rfloor 2n+1
奇数x,第一根和n的右边有连接的 x 为 ⌊ n − 1 3 + 1 ⌋ \lfloor \frac{n-1}{3}+1 \rfloor 3n1+1 ; 最后根据x的奇偶性分类讨论一下。

#include
using namespace std;
long long n,ans=0,x,d;
int main()
{
    int t; scanf("%d",&t);
    while(t--)
    {
        ans=0;
        scanf("%lld",&n);
        ans=ans+(n+1)/2; // 偶数部分 
        
        // 奇数部分 
        x=(n-1)/3+1;
        if(x%2==1){
            ans+=(n-x)/2+1;
        }
        else{
            ans+=(n-x+1)/2;
        }
        printf("%lld\n",ans);
    }
}

基本题

1009题 Command Sequence / 指令序列

题目大意
有一种机器人可以通过接收一系列命令来移动。
命令序列中有四种类型的命令:
U:机器人向上移动一个单位。
D:机器人向下移动一个单位。
L:机器人向左移动一个单位。
R:机器人向右移动一个单位。

现在,给定一个长度为 n n n ( 2 ≤ n ≤ 1 0 5 2≤n≤10^5 2n105) 的命令序列。你需要找出命令序列中有多少连续子串,满足机器人执行子字符串命令时可以返回到起始位置。

考察内容
前缀和,map

分析
前缀和预处理当前位置出现的次数。向左右可以看作向上下走了1e6。
把每个位置当作子串结尾进行遍历,每次遍历到时,ans累计map之前的值。
最后输出ans即可。

#include 
#include 
#include 
using namespace std;

map<long long,long long>m;
long long t,n,v,z,ans;
char c[200005];

int main() {
	scanf("%lld",&t);
	while(t--) {
		scanf("%lld",&n);
		scanf("%s",c);
		v=0;
		ans=0;
		m.clear();
		m[0]=1;
		for(int i=0;i<n;i++) {
			if(c[i]=='U') {
				z=1;
			} else if(c[i]=='D') {
				z=-1;
			} else if(c[i]=='L') {
				z=1e6; 
			} else if(c[i]=='R') {
				z=-1e6;
			}
			v+=z;
			ans+=m[v];
			
			m[v]=m[v]+1;
		}
		printf("%lld\n",ans);
	}
	return 0;
}

1006题 Power Sum / 平方和

题目大意
给定正数 n n n ,
求一个长度为 k ( 1 ≤ k ≤ n + 2 ) k(1≤k≤n+2) k(1kn+2) 的数组 a i ( a i ∈ { − 1 , 1 } ) {a_i}(a_i∈\{−1,1\}) ai(ai{1,1}) ,使 ∑ i = 1 k a i × i 2 = n ∑_{i = 1}^k a_i×i^2 = n i=1kai×i2=n

考察内容
思维,构造

分析
( x + 1 ) 2 − x 2 = 2 x + 1 (x+1)^2-x^2=2x+1 (x+1)2x2=2x+1
( x + 3 ) 2 − ( x + 2 ) 2 = 2 x + 5 (x+3)^2-(x+2)^2=2x+5 (x+3)2(x+2)2=2x+5
所以 ( x + 3 ) 2 − ( x + 2 ) 2 − ( x + 1 ) 2 + x 2 = 4 (x+3)^2-(x+2)^2-(x+1)^2+x^2=4 (x+3)2(x+2)2(x+1)2+x2=4
i ≥ 4 i≥4 i4时,构造 a i a_i ai 1 , − 1 , − 1 , 1 , . . . 1,-1,-1,1,... 1,1,1,1,... 的情况,就可以保证正确性。

#include
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
using namespace std;
const int N=1e6+10;
ll n,m,a[N];
string s;
int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>n;
		if(n%4==1){
			cout<<n<<endl;
			cout<<'1';
			for(int i=1;i<=n/4;i++){
				cout<<"1001";
			}
			cout<<endl;
		} 
		else if(n%4==2){
			cout<<n+2<<endl;
			cout<<"0001";
			for(int i=1;i<=n/4;i++){
				cout<<"1001";
			}
			cout<<endl;
		} 
		else if(n%4==3){
			cout<<n-1<<endl;
			cout<<"01";
			for(int i=1;i<=n/4;i++){
				cout<<"1001";
			}
			cout<<endl;
		} 
		else if(n%4==0){
			cout<<n<<endl;
			for(int i=1;i<=n/4;i++){
				cout<<"1001";
			}
			cout<<endl;
		} 
	}
	return 0;
}

1002题 Time-division Multiplexing / 时分多路转换

题目大意
给定一个小写字母组成的字符串,求最短的包含其中所有字母的连续子串的长度。

考察内容
双指针

分析
首先记录总字符个数。
跑一遍双指针。右指针指向子串末尾,不断右移。当出现的不同字符个数等于总字符个数时,更新答案,然后指向子串头部的左指针右移。
复杂度 O ( n ) O(n) O(n)

在双指针部分,注意控制一下边界情况,不要产生数组越界。

#include
#define ll long long
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'
using namespace std;
const int N=110;
ll n,m;
string s[N];
int len[N];

int num[27];
int has[27];
int kind=0,tp;

ll gcd(ll a, ll b){
    return b == 0 ? a : gcd(b, a % b);
}

void init(int n){
	memset(has,0,sizeof(has));
	memset(num,0,sizeof(num));
	memset(len,0,sizeof(len[0])*(n+1));
}

int main(){  
	ios::sync_with_stdio(0); cin.tie(0);
	int t; cin>>t;
	while(t--){
		cin>>n;
		init(n);
		
		for(int i=1;i<=n;i++){
			cin>>s[i];
			len[i]=s[i].size();
			for(int j=0;j<len[i];j++){
				int t1=s[i][j]-'a';
				has[t1]=1; //
			}
		}
		
		kind=0;
		for(int i=0;i<26;i++){
			if(has[i]) kind++;
		}
		
		ll high=1;
		for(int i=1;i<=n;i++){
			high=(high*len[i])/gcd(high,len[i]);
		}
		//	high<=27720,即(5*7*8*9*11)
		
		string str; // 
		for(int i=0;i<high;i++){ // 
			for(int j=1;j<=n;j++){
				int t2=i%len[j];
				str += s[j][t2];
			}
		}
		str+=str; // 两倍 
		
		ll ans=1e9+10;
		ll p1=0,p2=0; //
		int t3=str[p2]-'a';
		num[t3]++; //
		tp=1;
		
		int F1=0;
		
		int len2=high*n*2; //
		
		while(p1<len2 && p2<len2){ //
			if(tp==kind){ // 
				ans=min(ans,p2-p1+1);
				if(ans<=kind){ //
					cout<<ans<<endl;
					F1=1;
					break;
				}	
				
				int t4=str[p1]-'a';
				num[t4]--;
				if(num[t4]==0)tp--;
				
				p1++;
			}
			else{
				p2++;
				
				if(p2>=len2) break; // 防止数组越界 
				
				int t5=str[p2]-'a';
				if(num[t5]==0)tp++;
				num[t5]++;
			}
		}
		
		if(F1==0){
			cout<<ans<<endl;
		} 
	}
	return 0;
}

你可能感兴趣的:(算法竞赛,算法,c++,acm竞赛)