Loading

CodeTon Round 2 (Div. 1 + Div. 2, Rated, Prizes!)

\(\texttt{Rating Change:}\color{orange}{2213}\color{black}\to \color{orange}{2225}\)
\(\Delta={\color{green}{\texttt{12}}}\qquad \texttt{rank:510}\)

最近质量最高的一场 Chinese Round 吧。

A

可以转化成每次将前面两个中选一个留下。这样也就是说只有 \(b\) 的第一位可以和 \(a\) 相应的后缀不同。并且如果不同必须要是 \(a\) 此前出现过的。

My Code
using namespace std;
const int MAXN=55;
void solve(){
	int n,m;cin>>n>>m;string a,b;
	cin>>a>>b;a=' '+a;b=' '+b;
	int ta=n,tb=m;
	while(a[ta]==b[tb]&&tb>=1) ta--,tb--;
	if(tb>1) cout<<"NO\n";
	else if(tb==0) cout<<"YES\n";
	else{
		per(i,ta,1) if(a[i]==b[1]){
			cout<<"YES\n";
			return;
		}cout<<"NO\n";
	}
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)solve();
	return 0;
}

B

一开始确定一个可选的 \(v\) 的区间,然后将这个区间不停地加上当前的限制。直到区间为空的时候,使用一次 change,然后继续这样做,显然是最少的。

My Code
#define int long long
using namespace std;
const int MAXN=2e5+10;
int a[MAXN];
void solve(){
	int n,x;cin>>n>>x;
	rep(i,1,n) cin>>a[i];
	int l=a[1]-x,r=a[1]+x,ans=0;
	rep(i,2,n){
		if((a[i]-x>=l&&a[i]-x<=r)||(a[i]-x<l&&a[i]+x>=l))
			l=max(l,a[i]-x),r=min(r,a[i]+x);
		else ans++,l=a[i]-x,r=a[i]+x;
	}cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)solve();
	return 0;
}

C

相当于每次我们可以把一处感染堵上。这样我们把所有未感染的段全部取出来,从大到小贪心地去堵。实在太短,已经被感染的就摆,这样肯定能最大限度地救人。

My Code
#define int long long
using namespace std;
const int MAXN=1e5+10;
int a[MAXN];
void solve(){
	int n,m;cin>>n>>m;
	rep(i,1,m) cin>>a[i];
	sort(a+1,a+1+m);
	vector<int> cnt;
	rep(i,2,m) cnt.pb(a[i]-a[i-1]-1);
	cnt.pb(a[1]+n-a[m]-1);
	sort(all(cnt));reverse(all(cnt));
	int del=0,alv=0;
	for(int v:cnt){
		if(v-del>1) alv+=v-del-1,del+=4;
		else if(v-del==1) del+=2,alv++;
		else break;
	}cout<<n-alv<<'\n';cerr<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)solve();
	return 0;
}

D

挺智慧的题。考虑操作一和操作二的不同之处。如果我们对于每个数组构造一个键值:

\[s_i=\sum_{j=1}^m c_{i,j}\times j \]

这样子对于操作一键值是不变的,而操作二会加一。所以我们直接算出每个数组的 \(s\),取最大的,然后与其他的差就是最少的操作二的次数。

My Code
using namespace std;
const int MAXN=1e5+10;
unsigned ll a[MAXN];
void solve(){
	int n,m,x,mxid=0;cin>>n>>m;
	unsigned ll mx=0;
	rep(i,1,n){
		a[i]=0;
		rep(j,1,m)
			cin>>x,a[i]+=1ll*x*j;
		if(a[i]>mx) mx=a[i],mxid=i;
	}cout<<mxid<<' '<<a[mxid]-a[mxid==1?2:1]<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)solve();
	return 0;
}

E

半路改题面差评。

发现如果每个点没有一个后继是 \(0\) 的话,就不会出现减一的点集变化或者加一的点集变化的情况。那我们就可以直接对每个点算贡献。

此时并不一定满足。但是我们可以发现,先暴力跑 \(n\) 次,可以使得这张图满足上述条件,就做完了。

My Code
#define int long long
using namespace std;
const int MAXN=1010;
const int MOD=998244353;
vector<int> e[MAXN],fr[MAXN];
int v[MAXN],cnt[MAXN],deg[MAXN];
void solve(){
	int n,m;cin>>n>>m;
	rep(i,1,n) cin>>v[i],e[i].clear(),fr[i].clear(),cnt[i]=deg[i]=0;
	rep(i,1,m){
		int x,y;cin>>x>>y;
		e[x].pb(y);fr[y].pb(x);deg[x]++;
	}
	int ed=0;
	rep(i,1,n) if(!siz(e[i])){ed=i;break;}
	rep(Cnt,1,1000){
		vector<int> x;
		rep(i,1,n) if(v[i]) x.pb(i);
		if(!siz(x)){
			cout<<Cnt-1<<'\n';
			return;
		}for(int i:x){
			v[i]--;
			for(int s:e[i])
				v[s]++;
		}
	}
	queue<int> q;q.push(ed);cnt[ed]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int s:fr[x]) (cnt[s]+=cnt[x])%=MOD;
		for(int s:fr[x])
			if(--deg[s]==0)
				q.push(s);
	}
	int ans=1000;
	rep(i,1,n){
		ans=(ans+cnt[i]*v[i]%MOD)%MOD;
	}cout<<ans<<'\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	int T;for(cin>>T;T--;)solve();
	return 0;
}

F

博弈论是我不会的东西。

首先可以发现如果 RB 数量不相等,那么肯定是多的那一方赢了。因为起初两方肯定取 RB 或者 BR。当没有红蓝相邻的时候,只能取自己的那种颜色了。此时就是谁多谁活得久。

所以现在只需要讨论一下两者颜色相同的情况。我们把每一段红蓝相间的拉出来,我们只需要考虑这些段的胜负即可。

利用 \(SG\) 函数和,令 \(SG(len)\) 表示长度为 \(len\) 的红蓝段的 \(SG\) 值。这样直接求是 \(O(n^2)\) 的。然后打表发现实际上在长度很长的时候是以 \(34\) 为一循环的,所以只要预处理前一段即可。

My Code
using namespace std;
const int MAXN=5e5+10;
int sg[155],ha[155];
void init(){
	rep(i,2,150){
		memset(ha,0,sizeof(ha));
		rep(j,1,i-1) ha[sg[j-1]^sg[i-j-1]]=1;
		while(ha[sg[i]]) sg[i]++;
	}
}
void solve(){
	int n;cin>>n;
	string s;cin>>s;s=' '+s;
	int cntr=0,cntb=0,ans=0;
	rep(i,1,n) if(s[i]=='R') cntr++;else cntb++;
	for(int l=1,r;l<=n;l=r){
		r=l+1;while(r<=n&&s[r]!=s[r-1]) r++;
		if(r==l+1) continue;
		int tmp=r-l;
		while(tmp>150) tmp-=34;
		ans^=sg[tmp];
	}
	if(cntr>cntb||(cntr==cntb&&ans))
		cout<<"Alice\n";
	else cout<<"Bob\n";
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	init();
	int T;for(cin>>T;T--;)solve();
	return 0;
}
posted @ 2022-08-01 15:22  ZCETHAN  阅读(45)  评论(0编辑  收藏  举报