2021年CCPC网络赛(原赛和复赛)

1001.Cut The Wire

分析:
一道很简单的签到题;只需要分奇数和偶数两种情况找规律即可。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;cin>>t;
    while(t--){
        int n,ans=0;cin>>n;
        ans+=(n+1)/2;
        if(n&1) ans+=(n-(n-1)/3+1)/2;
        else     ans+=(n-(n-1)/3)/2;
        cout<<ans<<endl;
    }
    return 0;
} 

1002.Time-division Multiplexing

分析:
注意到每个时隙的字符串长度不超过 12,因此所有时隙总的循环周期是所有字符串长度的 lcm,即最小公倍数,所以所有可能循环的字符串长度为lcm(1,2,3,4,5,6,7,8,9,10,11,12)=27720。也就是说对于这n个串来说,他们构成的最大的最小周期串长度是27720 * n;(n<100)。需要注意的是,这个字符串是一个有循环节的无限长的串,因此需要对两倍长度的循环节求解。所以当我们构造出了这个两倍长度的循环节串s以后,只需要用双指针法或者二分法求解即可。关键在于构造该字符串s。注意:该二分法枚举的是区间的长度,将区间整体右移。

AC代码如下:
二分法

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn=6e6+9;
int n,ans,kind,sum;
int pos[105],len[105],num[30];
bool vis[100];
string s,str[105];
void Init(){
	memset(len,0,sizeof(len));
	memset(pos,0,sizeof(pos));	
	s.clear();ans=INF;kind=sum=0;
	for(int i = 0;i <= 26 ; i ++) vis[i]=false;
	for(int i = 1;i <= n ; i ++){
		cin>>str[i];
		len[i]=str[i].length();
		for(int j=0;j<len[i];j++){
			if(!vis[str[i][j]-'a']){
				vis[str[i][j]-'a']=true;
				kind++;
			}
		}
	}
}
inline bool checkS(){
	for(int i=1;i<=n;i++){
		if(pos[i]!=0) return true;
	}
	return false;
} 
void creat(){
	do{
		for(int i=1;i<=n;i++){
			s+=str[i][pos[i]];
			pos[i]=(pos[i]+1)%len[i];
		}
		sum+=n;
	}while(checkS());
	s=s+s;
	sum*=2;
}
bool check(int mid){
	memset(num,0,sizeof(num));
	int flag=0;
	for(int i=0;i<mid;i++){
		if(!num[s[i]-'a']) flag++;
		num[s[i]-'a']++; 
	}
	if(flag==kind) return true;
	int l=0;
	for(int r=mid;r<sum;r++,l++){
		if(--num[s[l]-'a']==0) --flag;
		if(++num[s[r]-'a']==1) ++flag;
		if(kind==flag) return true; 
	}
	return false;
}
int main(){
	iostream::sync_with_stdio(false); 
	int t;cin>>t;
	while(t--){		
	    cin>>n;
	    Init(); creat();
		int l=kind,r=sum;
        while(l<r){
        	int mid=(l+r)/2;
    	    if(check(mid)) r=mid;
    	    else l=mid+1;
	    }
	    cout<<l<<endl;
	}
	return 0;
} 

双指针法

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn=6e6+9;
int n,ans,kind,sum;
int pos[105],len[105],num[105];
bool vis[100];
string s,str[105];
void Init(){
	memset(len,0,sizeof(len));
	memset(num,0,sizeof(num));
	memset(pos,0,sizeof(pos));	
	s.clear();ans=INF;kind=sum=0;
	for(int i = 0;i <= 26 ; i ++) vis[i]=false;
	for(int i = 1;i <= n ; i ++){
		cin>>str[i];
		len[i]=str[i].length();
		for(int j=0;j<len[i];j++){
			if(!vis[str[i][j]-'a']){
				vis[str[i][j]-'a']=true;
				kind++;
			}
		}
	}
}
inline bool checkS(){
	for(int i=1;i<=n;i++){
		if(pos[i]!=0) return true;
	}
	return false;
} 
void creat(){
	do{
		for(int i=1;i<=n;i++){
			s+=str[i][pos[i]];
			pos[i]=(pos[i]+1)%len[i];
		}
		sum+=n;
	}while(checkS());
	s=s+s;
	sum*=2;
}
int main(){
	iostream::sync_with_stdio(false); 
	int t;cin>>t;
	while(t--){		
	    cin>>n;
	    Init(); creat();
        int l=0,r=0,cnt=0;
        while(l<sum&&r<sum){
        	while(r<sum&&cnt<kind){
        		if(num[s[r]-'a']==0) cnt++;
        		num[s[r++]-'a']++;
			}
			if(cnt!=kind) break;
			while(l<r&&cnt==kind){
				num[s[l]-'a']--;
				if(num[s[l++]-'a']==0) cnt--;
			}
			ans=min(ans,r-l+1);
		}
		cout<<ans<<endl;
	}
	return 0;
} 

1005.Power Sum

分析:
通过找规律,可以发现一个等式 x^ 2-(x+1)^ 2-(x+2)^ 2+(x+3)^ 2==4;
所以也就是说字符串"1001"就是一个4;而我们也可以通过前四个数字平方的组合,也就是[1,4,9,16]构造出1,2,3的值。显然,1可以用“1”代替;2可以用"0001"代替,3可以用"01"代替。对于任意一个数x, x%4=[0,1,2,3];所以很显然我们可以通过字符串构造出所有的可能。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int t;cin>>t;
    while(t--){
       int n;cin>>n;
       if(n%4==1) {
             cout<<1+n/4*4<<endl;
             cout<<"1";
       }
       else if(n%4==2) {
             cout<<4+n/4*4<<endl;
             cout<<"0001";
       }
       else if(n%4==3) {
             cout<<2+n/4*4<<endl;
             cout<<"01";
       }
       else cout<<n/4*4<<endl;
       for(int i=1;i<=n/4;i++) cout<<"1001";
       cout<<endl;
    }
    return 0;
} 

1006.Function

分析:
对于 f(x)=Ax^ 2 g(x) +Bx^ 2+Cxg^ 2(x)+Dxg(x)来说,f(x)=(A*g(x)+B)x^ 2+(Cg^ 2(x)+Dg(x))x
因为x属于[1,1e6], g(x)max = g(999999) = 54 。也就是说
g(x)只有54种不同的值
。我们可以枚举g(x)的每一个取值,那么f(x)就是一个 普通的二次函数f(x)=a* x * x+b*x; 此时,只需要讨论一下a的取值情况(a>0,a=0,a<0);然后对于(a!=0)的二次函数求最小值来说,由于它不是单调问题,二分法不好用,但是由于它是有单峰值的函数,所以可以用三分法解决。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
vector<ll>v[100];
ll A,B,C,D,n;
inline ll f(ll a,ll b,ll x){
	return a*x*x+b*x;
}
inline ll g(ll x){
	int res=0;
	while(x){
		res+=x%10;
		x/=10;
	}
	return res;
}
void pre(){
	for(ll i=1;i<=1e6;i++){
		v[g(i)].push_back(i);
	}
}
void solve(){
	ll ans=1e18;
	for(ll i=1;i<=54;i++){
		ll l=0;
		ll r=upper_bound(v[i].begin(),v[i].end(),n)-v[i].begin()-1; //找到g(x)=i的第一个大于n的数的前一个(最大的小于n的数) 
		if(r<0) continue;
		ll a=A*i+B,b=C*i*i+D*i;
		if(a<=0) ans=min(ans,min(f(a,b,v[i][0]),f(a,b,v[i][r])));
		while(l<=r){
			ll mid1=l+(r-l)/3;
			ll mid2=r-(r-l)/3;
			ll res1=f(a,b,v[i][mid1]),res2=f(a,b,v[i][mid2]);
			if(res1<res2) r=mid2-1;
			else l=mid1+1;
			ans=min(ans,min(res1,res2));
		}
	}
	cout<<ans<<endl;
}
int main(){
	iostream::sync_with_stdio(false); 
	pre();
	int t;cin>>t;
	while(t--){
		cin>>A>>B>>C>>D>>n;
		solve();
	}
	return 0;
} 

1009.Command Sequence

分析:
当机器人回到了原处,也就是说机器人走了一个周期。我们可以用二维数组mp[u][l]记录当前机器人的状态,也就是上下方向状态为u和左右方向状态为l。当两个机器人状态相同时,也就是走过了一个周期,那么答案ans++需要注意的是,当状态为(u,l)的时候,如果前面的字符串中出现了x次(u,l)的状态,那么我们应该将ans+=x。所以我们可以用mp[u][l]来记录当前状态(u,l)出现的次数,也就是不断让mp[u][l]++。记住,由于n<=1e5,所以二维数组mp[][]应该用嵌套的map来维护。由于初态机器人位置为(0,0),需要将mp[0][0]初始化为1

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,map<int,int>>mp;
int main(){
	int t;cin>>t;
	while(t--){
		mp.clear();mp[0][0]=1;
		int n;cin>>n;
		ll ans=0;
		string s;cin>>s; 
		int u=0,l=0;
	    for(int i=0;i<n;i++){
	    	if(s[i]=='U') u++;
	    	else if(s[i]=='D') u--;
	    	else if(s[i]=='L') l++;
	    	else l--;
	    	ans+=mp[u][l];
	    	mp[u][l]++;
		}
		cout<<ans<<endl;
	}
	return 0;
} 
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

&が&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值