题解:Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)

总体情况

这次手速够快:ABC in 10min,ABCDE in 30min。
image
%%%%%%%%
image
image
%%%%%%%%

A - Raise Both Hands

思路

分类讨论很简单的。

注意一定要判断 \(0,0\) 这种情况。

Code

// Problem: A - Raise Both Hands
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_a
// Memory Limit: 1024 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	signed main(){
		int a,b;
		cin >> a>>b;
		cout << (a&&b?"Invalid":(a?"Yes":(b?"No":"Invalid")));
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}

B - Binary Alchemy

思路

这个直接模拟。

Code

// Problem: B - Binary Alchemy
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_b
// Memory Limit: 1024 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	const int MAXN =120;
	int n,a[MAXN][MAXN];
	signed main(){
		read(n);
		for(int i = 1;i<=n;i++){
			for(int j = 1;j<=i;j++) read(a[i][j]);
		}
		int now = 1;
		for(int i = 1;i<=n;i++){
			if(now>=i) now = a[now][i];
			else now = a[i][now];
		}
		write(now);
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}

C - Word Ladder

思路

最小此时明显就是每一个位置上面不一样的位置之和。

于是我们只需要保证字典序最小就行。

考虑怎么做到字典序最小。

  • 对于 \(a_i>b_i\) 的操作,它一定会将字典序变小,我们一定要先执行这些操作,并按照 \(i\) 从小到大执行。
  • 对于 \(a_i<b_i\) 的操作,它一定会将字典序变大,我们一定要后执行这些操作,并按照 \(i\) 从大到小执行。

Code

// Problem: C - Word Ladder
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_c
// Memory Limit: 1024 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	string a,b;
	vector<int> v1,v2;
	signed main(){
		cin >> a >>b;
		for(int i = 0;i<a.size();i++){
			if(a[i]==b[i]) continue;
			if(a[i]<b[i]) v2.push_back(i);
			else v1.push_back(i);
		}
		reverse(v2.begin(),v2.end());
		cout << v1.size()+v2.size() << endl;
		for(int i:v1){
			a[i] = b[i];
			cout << a << endl;
		}
		for(int i:v2){
			a[i] = b[i];
			cout << a << endl;
		}
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}

D - Cross Explosio

思路

对于每一个 \(x\)\(y\) 都建立一个 set,直接使用模拟即可。

如果存在点,我们直接删除。

如果不存在这个点,对于 \(x\) 上面,找到前面的一个下标和后面的一个下标删除点就行,在 \(y\) 上同理。

Code

// Problem: D - Cross Explosion
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_d
// Memory Limit: 1024 MB
// Time Limit: 4000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	const int MAXN = 4e5+10;
	int n,m,q;
	set<int> stx[MAXN],sty[MAXN];
	signed main(){
		read(n);read(m);read(q);
		for(int i = 1;i<=n;i++){
			for(int j = 1;j<=m;j++){
				stx[i].insert(j);
				sty[j].insert(i);
			}
		}
		for(int i = 1;i<=q;i++){
			int a,b;
		// int ans = 0;
		// for(int i = 1;i<=n;i++) ans += stx[i].size();
		// write(ans,endl);
			read(a);read(b);
			if(stx[a].find(b)!=stx[a].end()){
				stx[a].erase(b);
				sty[b].erase(a);
			}else{
				if(1){
					auto tmp2 = stx[a].upper_bound(b);
					if(tmp2!=stx[a].begin()){
						auto tmp1 = --tmp2;tmp2++;
						stx[a].erase(*tmp1);
						sty[*tmp1].erase(a);
					}
					if(tmp2!=stx[a].end()){
						stx[a].erase(*tmp2);
						sty[*tmp2].erase(a);
					}
				}
				if(1){
					auto tmp2 = sty[b].upper_bound(a);
					if(tmp2!=sty[b].begin()){
						auto tmp1 = --tmp2;tmp2++;
						sty[b].erase(*tmp1);
						stx[*tmp1].erase(b);
					}
					if(tmp2!=sty[b].end()){
						sty[b].erase(*tmp2);
						stx[*tmp2].erase(b);
					}
				}
			}
		// ans = 0;
		// for(int i = 1;i<=n;i++) ans += stx[i].size();
		// write(ans,endl);
		}
		int ans = 0;
		for(int i = 1;i<=n;i++) ans += stx[i].size();
		write(ans);
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}

E - Avoid K Partition

思路

一眼 DP。跟这道题差不多。

定义 \(f_i\) 表示的是以 \(i\) 结尾的方案数。

则可以设计转移:

\[f_i=\sum_{s(j,i)\neq k} f_j \]

其中 \(s(i,j)\) 代表 \([i,j]\) 的数的和。

这个是 \(O(n^3)\) 的,明显会炸。

首先我们可以改变整个 DP 的转移。

就是所有方案之和减去可以使得 \(s(j,i)=k\) 的方案数。

可以使用前缀和拆分开,\(sum_i-sum_{j-1}=k\) 的方案数,就是求 \(sum_{j-1}=sum_i-k\) 的方案数,可以使用一个桶进行记录。定义 \(g_i\) 代表的是 \(sum_j=i\) 的方案数,于是:

\[f_i=\sum_{j\in[1,i)} f_j -g_{sum_i-k} \]

但是,\(sum_i-k\) 一定不能取模,我们稍微算一下,\(1\times 10^{15} \cdot 2\times 10^5=2\times 10^{20}\),是需要开 __int128 的。

于是时间复杂度变为 \(O(n^2)\)

其中 \(\displaystyle \sum_{j\in[1,i)} f_j\) 直接使用变量 \(summ\) 存储就行。

时间复杂度:\(O(n)\)

不要看见这些怎么多的推理,其实考试的时候一念之间就从 \(O(n^3)\) 优化到了 \(O(n)\))。

Code

// Problem: E - Avoid K Partition
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	const int MAXN = 2e5+10;
	const int MOD = 998244353;
	int n,k,sum;
	__int128 summ;
	int f,a[MAXN];
	map<__int128,int> mp;
	signed main(){
		read(n);read(k);
		sum = 1;
		mp[0] = 1;
		for(int i = 1;i<=n;i++){
			read(a[i]);
			summ += a[i];
			f = sum-mp[summ-k];
			mp[summ] += f;
			mp[summ] %= MOD;
			sum += f;
			sum %= MOD;
			// cout << f << " " << sum << " " << summ << endl;
		}
		write((f%MOD+MOD)%MOD);
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}

F - Cake Division

思路

首先,对于这个问题,一看就知道要使用二分答案来解决。

于是我们就将问题转换成将蛋糕分给 \(k\) 个人,使得每个人都能至少得到权值为 \(w\) 的方案是否可行。如果可以,那么那些边一定不能删。

我们先来看是否可行的问题。

首先我们可以先破环为链,预处理出来从第 \(i(i\in[1,n])\) 个蛋糕开始满足 \(sum(i,j)\ge w\) 的最小值,记录为 \(f(i)\),如果不存在,就是 \(f(i)=\infty\)。我们枚举第一个人先拿的是那一块蛋糕,那么我们一定会给他分配到第 \(f(i)\) 块蛋糕,接着第二个人就从 \(f(i)+1\) 开始拿蛋糕,一直到 \(f(f(i)+1)\) 块蛋糕,可以证明这个定是分配时最优秀的情况。只要有一个 \(i\) 满足要求,那么这个 \(w\) 就是可行的。

但是暴力这样的时间复杂度是 \(O(n^2\log_2 n+nk\log_2 n)\) 的,会超时。

于是我们可以试着给 \(i\)\(f(i)+1\) 连一条有向边,那么求得就是 \(i\) 向前走的第 \(k-1\) 个父亲。这个可以使用倍增解决。

而关于怎么求 \(f(i)\),直接使用双指针求就行,这样时间复杂度被我们降为了 \(O(n\log^2_2 n)\) 的,非常理想。

所以我们就已经知道了最大的 \(w\),已经解决了第一问。

对于最大的 \(w\),我们已经知道 \(w\) 对于那个(些)\(i\) 是满足限制的。我们可以对 \(i\) 打一个标记,于是问题变成了在一个 DAG 里面,有一些点上面有标记,标记会顺着边一路往前,求有那些点没有被打过标记。

这个问题很经典,就是一个拓扑序 DP。实际上我们只关心 \(i\) 的下一个是谁,这样就可以递推进行了。

我的思路很复杂,代码可能有一点难写。

Code

// Problem: F - Cake Division
// Contest: AtCoder - Toyota Programming Contest 2024#9(AtCoder Beginner Contest 370)
// URL: https://atcoder.jp/contests/abc370/tasks/abc370_f
// Memory Limit: 1024 MB
// Time Limit: 5000 ms

#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
//#define int long long
#pragma GCC optimize(2)
namespace gtx{
//	Fast IO
	void read(int &x){
		x = 0;int h = 1;char tmp;
		do{tmp=getchar();if(tmp=='-')h*=-1;}while(!isdigit(tmp));
		while(isdigit(tmp)) x*=10,x+=tmp-'0',tmp=getchar();
		x*=h;
	}
	void read(char &x){do{x=getchar();}while(x==' '||x=='\n'||x=='\r');}
	void write(char x){putchar(x);}
	void write(int x){
		if(x<0) putchar('-'),x=-x;int st[200]={0},tot=0;
		do{st[++tot]=x%10,x/=10;} while(x);
		while(tot){putchar(st[tot--]+'0');};
	}
	void write(int x,char y){write(x);write(y);}
	const int MAXN = 4e5+10;
	const int LOGN = log2(MAXN)+2;
	const int INF = 0x3f3f3f3f;
	int n,k;
	int fath[MAXN][LOGN],sum[MAXN];
	bitset<MAXN> b;
	int get(int x,int w){
		int k = 0;
		while(w){
			if(w&1){
				if(x!=INF) x = fath[x][k];
			}
			w>>=1;
			k++;
		}
		return x;
	}
	bool check(int w){
		for(int i = 1;i<=2*n+5;i++) for(int j = 0;j<LOGN;j++) fath[i][j] = INF;
		int end=1;
		for(int i = 1;i<=2*n;i++){
			while(end<=2*n&&(sum[end]-sum[i-1])<w) end++;
			end = min(end,2*n);
			if((sum[end]-sum[i-1])<w) fath[i][0] = INF;
			else fath[i][0] = end+1;
		}
		for(int j = 1;j<LOGN;j++){
			for(int i = 1;i<=2*n;i++){
				fath[i][j] = (fath[i][j-1]==INF)?INF:fath[fath[i][j-1]][j-1];
			}
		}
		for(int j = 1;j<=n;j++){
			if(get(j,k)-1<j+n) return true;
		}
		return false;
	}
	signed main(){
		read(n);read(k);
		int p = 0;
		for(int i = 1;i<=n;i++){
			read(sum[i]);
			sum[i+n] = sum[i];
			p+=sum[i];
		}
		for(int i = 1;i<=2*n;i++) sum[i]+=sum[i-1];
		int l = 0,r = p/k,ans = 0;
		while(l<=r){
			int mid = (l+r)>>1;
			if(check(mid)){
				l = mid+1;
				ans = mid;
			}else r= mid-1;
		}
		write(ans,' ');
		for(int i = 1;i<=2*n+5;i++) for(int j = 0;j<LOGN;j++) fath[i][j] = INF;
		int end=1;
		
		for(int i = 1;i<=2*n;i++){
			while(end<=2*n&&(sum[end]-sum[i-1])<ans) end++;
			end = min(end,2*n);
			if((sum[end]-sum[i-1])<ans) fath[i][0] = INF;
			else fath[i][0] = end+1;
		}
		for(int j = 1;j<LOGN;j++){
			for(int i = 1;i<=2*n;i++){
				fath[i][j] = (fath[i][j-1]==INF)?INF:fath[fath[i][j-1]][j-1];
			}
		}
		for(int j = 1;j<=n;j++){
			if(get(j,k)-1<j+n){
				b.set(j);
			}
		}
		for(int j = 1;j<=2*n;j++){
			if(b[j]&&fath[j][0]!=INF) b[(fath[j][0]>n)?fath[j][0]-n:fath[j][0]]=1;
		}
		write(n-(int)b.count());
		return 0;
	}
}
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int T = 1;
//	gtx::read(T);
	while(T--) gtx::main();
	return 0;
}
posted @ 2024-09-07 21:51  GuTongXing  阅读(182)  评论(0)    收藏  举报