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;
}