ICPC 2021 昆明 解题记录

先开,下午写。
锐评:构造+神秘dp+大模拟+神秘dp
CF上面竟然没有!我竟然要用牛客交!于是我和ConanKID用的Soba的号交
其实他们两个改了用户名但是我拼不出来
注:下述题号以牛客中的重现赛为标准。

K

又是构造。
大概就是说你可以把这个抽象成很多个置换环。
然后在环里面一次消除就可以了!

#include<bits/stdc++.h>
using namespace std;
int n,a[100005],p[100005],cnt,x[100005],y[100005];
bool vis[100005];
bool flg;
void _swap(int u,int v){
//	cout<<u<<' '<<v<<' '<<vis[u]<<' '<<vis[v]<<'\n';
	x[++cnt]=u;y[cnt]=v;
	swap(a[u],a[v]);
	p[a[u]]=u;p[a[v]]=v;
	return;
}
void dfs(int cur){
	if(a[a[cur]]==cur)return;
	int u=a[cur],v=p[cur];
	_swap(u,v);
	vis[u]=vis[v]=1;
	dfs(v);
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0); 
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
		p[a[i]]=i;
	}
	int ans=0;
	for(int i=1;i<=n;++i){
		if(a[i]!=i)ans=1;
		if(a[a[i]]!=i){
			ans=2;
			break;
		}
	}
	cout<<ans<<'\n';
	while(ans--){
		memset(vis,0,sizeof(vis));
		cnt=0;
		for(int i=1;i<=n;++i){
			if(a[i]==i||vis[i])continue;
			if(a[a[i]]==i){
				if(vis[a[i]])continue;
				vis[i]=vis[a[i]]=1;
				_swap(i,a[i]);
			}
			else{
				vis[i]=1;
				dfs(i);
			}
		}
		cout<<cnt<<' ';
		for(int i=1;i<=cnt;++i)cout<<x[i]<<' '<<y[i]<<' ';
		cout<<'\n';
	}
	return 0;
}

I

大模拟/se/se/se
大概就是说,你可以枚举丢掉哪张牌,再枚举加进来哪张,再枚举那一对当对子用(题面的提示)
然后就以此判能不能三连和顺子就可以了!
有很多 \(\text{Corner Case}\)!!!
详见代码

#include<bits/stdc++.h>
using namespace std;
int t,card[35],n;
string s;
int num[5],_num[35],numm[35],nummm[5];
int calc(int x,char b){
	if(b=='w')return num[1]++,x;
	else if(b=='b')return num[2]++,x+9;
	else if(b=='s')return num[3]++,x+18;
	else return num[4]++,x+27;
}
pair<int,char> reca(int x){
	if(x>27)return make_pair(x-27,'z');
	else if(x>18)return make_pair(x-18,'s');
	else if(x>9)return make_pair(x-9,'b');
	else return make_pair(x,'w');
}
int change(char s){
	int x;
	if(s=='w')x=1;
	else if(s=='b')x=2;
	else if(s=='s')x=3;
	else x=4;
	return x;
}
int check(int i,int j,int k){
	_num[card[i]]--;
	_num[j]++;
	num[change(reca(card[i]).second)]--;
	num[change(reca(j).second)]++;
//	for(int i=1;i<=34;++i){
//		cout<<reca(i).first<<' '<<reca(i).second<<' '<<_num[i]<<"\n";
//	}
//	cout<<"-----------------------------------------------------\n";
	pair<int,char>p=reca(k);
	int x;
	x=change(p.second);
	_num[k]-=2;
	num[x]-=2;
	for(int i=28;i<=34;++i){
		int res=_num[i];
		if(res%3)return 0;
	}
	for(int i=1;i<=3;++i){
		int res=num[i];
		if(res%3)return 0;
	}
//	if(i==11&&j==19&&k==19)cout<<">?";
	for(int i=1;i<=34;++i){
		while(_num[i]>=3)_num[i]-=3;
		while(i<=25&&_num[i]>=1&&_num[i+1]>=1&&_num[i+2]>=1&&(i+1)/9==(i-1)/9){
			_num[i]--;_num[i+1]--;_num[i+2]--;
		}
	}
//	for(int i=1;i<=34;++i){
//		cout<<reca(i).first<<' '<<reca(i).second<<' '<<_num[i]<<"\n";
//	}
	for(int i=1;i<=34;++i){
		if(_num[i]!=0)return 0;
	}
//	cout<<'\n';
	return 1;
}
bool vis[35];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		memset(num,0,sizeof(num));
		memset(_num,0,sizeof(_num));
		cin>>s;
		n=0;
		for(int i=0;i<s.size();i+=2)card[++n]=calc(s[i]-'0',s[i+1]),_num[card[n]]++;
		sort(card+1,card+n+1);
//		cout<<"N: "<<n<<'\n';
		for(int i=1;i<=4;++i)nummm[i]=num[i];
		for(int i=1;i<=34;++i)numm[i]=_num[i];
		bool flgg=0;
		for(int i=1;i<=13;++i)
		if(card[i]==card[i+1]){
			if(check(1,card[1],card[i])){
				cout<<"Tsumo!\n";
				flgg=1;
				break;
			}
			else{
				for(int kk=1;kk<=4;++kk)num[kk]=nummm[kk];
				for(int kk=1;kk<=14;++kk)_num[card[kk]]=numm[card[kk]];
			}
		}
		if(flgg)continue;
		vector<int>ans[35];
		
//		cout<<check(4,19,19)<<"!!\n";
//		for(int kk=1;kk<=4;++kk)num[kk]=nummm[kk];
//		for(int kk=1;kk<=34;++kk)_num[kk]=numm[kk];
		for(int i=1;i<=n;++i){
			if( card[i] == card[i-1] )  continue;
			for(int j=1;j<=34;++j){
//				memset(vis,0,sizeof(vis));
				for(int k=1;k<=14;++k){
					if((card[k]==card[k+1]||card[k]==j)){
//						vis[card[k]]=1;
//						if( i == 4 && j == 19 && card[k] == 19 )  cout<<"111 "<<check(i,j,card[k])<<endl;
						bool flgggg=0;
						if(check(i,j,card[k])){
							ans[card[i]].push_back(j)/*,cout<<"Test: "<<i<<' '<<j<<'\n'*/;
							flgggg=1;
						}
						for(int kk=1;kk<=4;++kk)num[kk]=nummm[kk];
						bool flggg=0;
						for(int kk=1;kk<=14;++kk){
							if(card[kk]==j)flggg=1;
							_num[card[kk]]=numm[card[kk]];
						}
						_num[j] = numm[j];
						if(flgggg)break;
					}
				}
			}
		}
		int cntt=0;
		for(int i=1;i<=34;++i)if(ans[i].size())++cntt;
		cout<<cntt<<'\n';
		for(int i=1;i<=34;++i){
			if(ans[i].size()){
				pair<int,char>p=reca(i);
				cout<<p.first<<p.second<<' ';
//				int len=unique(ans[i].begin(),ans[i].end())-ans[i].begin();
				for(auto j:ans[i]){
//					int j=ans[i][k];
					p=reca(j);
					cout<<p.first<<p.second;
				}
				cout<<'\n';
			}
		}
	}
	return 0;
}

记得去重!

C

特殊的状态:\(dp_{l,r}\) 表示把 \([l,r]\) 全部变成和 \(l\) 一样的最小代价。
\(2\) 种情况:

  • dp[l][r]=min(dp[l][r-1],dp[l+1][r])+1;,把边边角角改了
  • dp[l][r]=min(dp[l][r],dp[l][x-1]+dp[x][r]);a[x]=a[l] 成立的时候。
    预处理之后区间dp即可。
#include<bits/stdc++.h>
using namespace std;
int t,n,a[5005],pos[5005],dp[5005][5005],to[5005];
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>t;
	while(t--){
		memset(dp,0x3f,sizeof(dp));
		cin>>n;
		for(int i=1;i<=n;++i)pos[i]=n+1;
		for(int i=1;i<=n;++i){
			cin>>a[i];
			if(a[i]==a[i-1]){
				--i;--n;
			}
		}
		for(int i=n;i>=1;--i){
			to[i]=pos[a[i]];
			pos[a[i]]=i;
			dp[i][i]=0;
		}
		for(int len=2;len<=n;++len){
			for(int l=1;l+len-1<=n;++l){
				int r=l+len-1;
				dp[l][r]=min(dp[l][r-1],dp[l+1][r])+1;
				int x=to[l];
				while(x<=r){
//					cout<<x<<'\n';
					dp[l][r]=min(dp[l][r],dp[l][x-1]+dp[x][r]);
					x=to[x];
				}
			}
		}
		cout<<dp[1][n]<<'\n';
	}
}

G

最自闭的题。
是两部分拼在一起的缝合怪。

第一部分:

\(dp_{i,j,k}\) 表示考虑到第 \(i\) 个人,将其设置为第 \(j\) 个做蛋糕,现在是第 \(k\) 天(所以 \(k_{max}=365\))。
然后你发现这个会炸空间。
然后你发现可以滚动。
然后你发现炸时间。
然后你发现可以优化状态!
你发现没选的人最多 \(m\) 个,然后 \(m_{max}=15\)
所以 \(dp_{i,j,k}\) 表示第 \(i\) 个人,\(j\) 个人没选,第 \(k\) 天。
然后你发现这样的 \(k\) 转移时有超级多的 \(\text{Corner Case}\)!!!
然后你还可以改状态。\(k\) 表示还剩的天数即可。
2月29号出生的人要当空气,因为TA不过生日(

第二部分:

买礼物。
你发现 \(m\) 只有 \(15\)!直接爆搜。
然后你写完了,过了样例,然后提示你通过了 \(16.67\%\) 的数据点后愉悦地 WA 了。
然后你发现我要按日期排序!!1
然后你又交了一发,TLE。
为什么呢?因为你重复跑了搜索,\(\Theta(T\times N\times 2^M)\) 的时间复杂度是不可接受的!
于是你记下来的答案。
于是你过了。
好耶!!!

#include<bits/stdc++.h>
#define int long long
using namespace std;
int t[13]={0,31,28,31,30,31,30,31,31,30,31,30,31},tt[13],T;
int n,dp[2][20][366],m,w,a[20],b[20];

struct DDD{
	int date, c, v;
	bool operator < (const DDD &rhs) const{
		return date < rhs.date;
	}
}d[505]; 

int change(int month,int day){
	return tt[month-1]+day;
} 
int len,ans;
bool vis[20];
void check(){
	int res=0,sum=0;
	for(int i=1;i<=m;++i)if(vis[i])res+=a[i],sum+=b[i];
	if(res>w)return;
	ans=max(ans,sum);
}
void dfs(int cur,int cnt){
	if(cnt==len){
		check();
		return;
	}
	if(cur==m+1)return;
	dfs(cur+1,cnt);
	vis[cur]=1;
	dfs(cur+1,cnt+1);
	vis[cur]=0;
}
int maxn[505],anss[505];
signed main(){
//	freopen("T4.in","r",stdin);
//	freopen("gift.out","w",stdout);
	ios::sync_with_stdio(0);
//	cin.tie(0);
//	cout.tie(0);
	for(int i=1;i<=12;++i)tt[i]=tt[i-1]+t[i];
	cin>>T;
	while(T--){
		memset(maxn,0xcf,sizeof(maxn));
		cin>>n>>m>>w;
		for(int i=1;i<=n;++i){
			char C;
			int mon,da;
			cin>>C>>C>>C>>C>>C>>mon>>C>>da>>d[i].c>>d[i].v;
			if(mon==2&&da==29){
				i--;n--;
				continue;
			}
			d[i].date=change(mon,da);
		}
		sort(d+1, d+1+n);
		memset(dp, 0xcf, sizeof(dp));
		dp[0][0][0] = 0;
		for(int i=1;i<=m;++i)cin>>a[i]>>b[i];
		for(int i=1;i<=n;++i){
			for(int j=0;j<m;++j){
				for(int k=0;k<=365;++k){
					dp[i%2][j][k] = -1e9;
					if( j >= 1 && k >= d[i].date - d[i-1].date ) 
						dp[i%2][j][k] = dp[(i+1)%2][j-1][k-(d[i].date-d[i-1].date)];
					if( k - (d[i].date - d[i-1].date - d[i].c) >= 0 && k - (d[i].date - d[i-1].date - d[i].c) <= 365 )
						dp[i%2][j][k] = max(dp[i%2][j][k], dp[(i+1)%2][j][k - (d[i].date - d[i-1].date - d[i].c)] + d[i].v); 
				}
			}
			for(int k=0;k<=365;++k){
				dp[i%2][m][k] = -1e9;
				if( k >= d[i].date - d[i-1].date ) 
					dp[i%2][m][k] = max(dp[(i+1)%2][m-1][k-(d[i].date-d[i-1].date)], dp[(i+1)%2][m][k-(d[i].date-d[i-1].date)]);
				if( k - (d[i].date - d[i-1].date - d[i].c) >= 0 && k - (d[i].date - d[i-1].date - d[i].c) <= 365 )
					dp[i%2][m][k] = max(dp[i%2][m][k], dp[(i+1)%2][m][k - (d[i].date - d[i-1].date - d[i].c)] + d[i].v); 
			}
		}
		for(int i=0;i<=m;i++)
		{
			for(int j=0;j<=365;j++)
			{
				maxn[i] = max(maxn[i], dp[n%2][i][j]);
			}
// 			cout<<"maxn[i] = "<<maxn[i]<<endl;
		} 
		int res=0;
		for(int i=0;i<=min(m,n);++i){
			len=i;
			dfs(1,0);
			anss[i]=ans;
		}
		for(int i=0;i<=m;++i){
			int ans1=maxn[i];
			ans=0;
			for(int j=0;j<=min(n,i);++j){	
// 				cout<<"j = "<<j<<" , ans = "<<ans<<"\n";
				res=max(ans1+anss[j],res);
			}
		}
		cout<<res<<'\n';
	}
	return 0;
}

完结撒花

posted @ 2023-01-10 20:52  Forever1507  阅读(18)  评论(0编辑  收藏  举报