2020 icpc 上海站+模拟+dp
Posted 钟钟终
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020 icpc 上海站+模拟+dp相关的知识,希望对你有一定的参考价值。
A、B题就忽略了。重点记录下C题,这代替的重点在于代码的实现。
思路很简单:在保证字典序最小的情况下,使得字母不存在环。
队友提醒可以用并查集去写,确实,并查集可用于判环。另一种思路看的知乎大佬写的,实现的很巧妙,都尝试了一下。
C. Phase Shift
用两个map记录对应关系。
m1记录字符串中s的字符可转化为字符串t中某字符。
m2记录字符串中t的字符可由原字符串s中某个字符得到。
具体解释见代码:
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
const int mod=1e9+7;
bool cmp(int a,int b)return a>b;
//priority_queue<int,vector<int>,greater<int>>q;
int n;
string s;
map<int,int>m1,m2;
int fd(int x)
while(m2[x]!=-1) x=m2[x]; //t中对应的s字符,从x出发不构成环
return x;
void solve()
cin>>n>>s;
s=" "+s;
string ss="";
for(int i=0;i<26;i++) m1[i]=m2[i]=-1; //都初始化为-1
for(int i=1;i<=n;i++)
//若t中字符在之前已产生对应关系,则直接添加到结果字符串ss
if(m2[s[i]-'a']!=-1) ss+=(char)(m2[s[i]-'a']+'a');
else
int g=-1;
for(int j=0;j<26;j++)
if(s[i]-'a'==j) continue; //字符不可和自身产生对应
if(m1[j]==-1&&fd(j)!=s[i]-'a') //s中可用的较小字符且从j出发不构成环
g=j;break;
if(g==-1)
for(int j=0;j<26;j++)
if(m1[j]==-1&&j!=s[i]-'a')
g=j;break;
m1[g]=s[i]-'a'; //分别记录
m2[s[i]-'a']=g;
ss+=char('a'+g);
cout<<ss<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
并查集写法:看了队友的代码,按照自己的想法敲了一下。
首先需要判环,构造的字符串间不能出现环。例如:ba,b前面可放a,而a的前面不可放b,应该跳过b放c,本题关键就是实现这个操作。就容易想到并查集了。
代码思路:
对于字符串中每一个字符,都可在26个字母中寻求合适的。
pre数组:记录t->s ; 字符t之前放入字符s
aft数组: 记录s->t
若两个字符的首领不同,则合并,两个数组分别记录,大小也要合并,因为在该集合囊括26个字符时,需要对集合的首领进行记录。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
const int mod=1e9+7;
bool cmp(int a,int b)return a>b;
//priority_queue<int,vector<int>,greater<int>>q;
int n,pre[30],aft[30],f[30],sz[30];
string s;
int r_find(int r)
if(r==f[r]) return f[r];
f[r]=r_find(f[r]);
return f[r];
void solve()
cin>>n>>s;
s=" "+s;
string ss="";
for(int i=0;i<26;i++) f[i]=i,aft[i]=pre[i]=-1,sz[i]=1;
//pre :t->s aft:s->t
for(int i=1;i<=n;i++)
if(pre[s[i]-'a']!=-1)
ss+=char(pre[s[i]-'a']+'a');continue;
for(int j=0;j<26;j++)
if(s[i]-'a'==j||aft[j]!=-1) continue;
int fx=r_find(s[i]-'a'),fy=r_find(j);
if(fx!=fy)
pre[s[i]-'a']=j,aft[j]=s[i]-'a';
f[fx]=fy;
sz[fy]+=sz[fx];
if(sz[fy]==26)
int p1=0,p2=0;
for(int k=0;k<26;k++)
if(aft[k]==-1) p1=k;
if(pre[k]==-1) p2=k;
pre[p2]=p1,aft[p1]=p2;
ss+=char(j+'a');
break;
cout<<ss<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
D. Walker
本题时思路想错了,诶,好笨。
有一个关键错误:并不是说两人相对着走,遇见后各自返回,这样的时间最优,因为各自回去的时间就无法控制了;因此枚举中点才是正解。因此很多种情况直接排除,采用二分去做。
还有就是由精度要求时,直接for循环可控制二分次数。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
const int mod=1e9+7;
bool cmp(int a,int b)return a>b;
//priority_queue<int,vector<int>,greater<int>>q;
//int r_find(int r)
//
// if(r==f[r]) return f[r];
// f[r]=r_find(f[r]);
// return f[r];
//
double n,p1,v1,p2,v2;
double ans;
void solve()
cin>>n>>p1>>v1>>p2>>v2;
ans=0;
if(p1>p2)
swap(p1,p2);swap(v1,v2);
//一个人走完全程
ans=min((p1+n)/v1,(n-p1+n)/v1,(p2+n)/v2,(n-p2+n)/v2);
//相对走
ans=min(ans,max((n-p1)/v1,p2/v2));
//走到l~r间的某个点返回
double l=p1,r=p2;
for(int i=1;i<=200;i++)
double mid=(l+r)/2;
double t1=min((mid-p1+mid)/v1,(p1+mid)/v1);
double t2=min((p2-mid+n-mid)/v2,(n-p2+n-mid)/v2);
ans=min(ans,max(t1,t2));
//cout<<fixed<<setprecision(12)<<ans<<endl;
if(t1>=t2) r=mid;
else l=mid;
cout<<fixed<<setprecision(12)<<ans<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
M. Gitignore
先分析下题意,看了好久没读懂:根目录无法忽略的前提下,有n个路径是可以忽略的,m个路径不能忽略,(也就是在打开m个路径时有多少文件展露在外面,不能折叠起来)。求最少可放在外面的行数。
模拟思路:
1.还是别用树状结构去存了,有点不好写。用map去存m个不可被忽视的各级路径。
2.再对n进行遍历,若是该级路径被标记过,则继续遍历下一级。
3.对于没被m条路经表示的层级,若已经被访问过已计入贡献,则下次遍历到时边不用再累加了。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
const int mod=1e9+7;
bool cmp(int a,int b)return a>b;
//priority_queue<int,vector<int>,greater<int>>q;
//int r_find(int r)
//
// if(r==f[r]) return f[r];
// f[r]=r_find(f[r]);
// return f[r];
//
int n,m;
string s1[1005],s;
map<string,int>mp,vis;
void solve()
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>s1[i];
for(int i=1;i<=m;i++)
cin>>s;
for(int j=0;j<s.length();j++)
if(s[j]=='/')
string tmp=s.substr(0,j);
mp[tmp]=1;
int ans=0;
for(int i=1;i<=n;i++)
string tmp=s1[i];
int flag=1;
for(int j=0;j<tmp.length();j++)
if(tmp[j]=='/')
string ss=tmp.substr(0,j);
if(mp[ss]) flag=1;
else
if(vis[ss]) flag=0;
else vis[ss]=1;
break;
ans+=flag;
cout<<ans<<endl;
signed main()
//ios;
int T;cin>>T;
while(T--)
solve();
return 0;
D. Caesar’s Legions
本题难点在于dp状态的设计。
dp[i][0][j][k]:
前i个数中1的数目为j个 其中有k个连续的1
dp[i][1][j][k]:
前i个数中2的数目为j个 其中有k个连续的1
状态的转移相比较不是本题的难点:对于新数字的添加,在0和1当中选取。
1.dp[i][t][j][k]=(dp[i][t][j][k]+dp[i-1][t][j-1][k-1])%mod;
添加1的情况
2.dp[i][!t][i-j][1]=(dp[i][!t][i-j][1]+dp[i-1][t][j][k])%mod;
这里的转移是一个小难点:
若是添加2,则是第i-1个字母中1出现了j次且最后连续出现了k次;
因此对于第i个字母中2的个数为i-j,对于新添加的2,只出现了1次。
#include <bits/stdc++.h>
#define int long long
#define endl '\\n'
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int inf=1e18;
const int N=7e5+5;
const int mod=1e8;
bool cmp(int a,int b)return a>b;
//priority_queue<int,vector<int>,greater<int>>q;
//int r_find(int r)
//
// if(r==f[r]) return f[r];
// f[r]=r_find(f[r]);
// return f[r];
//
int n,m,k1,k2,dp[205][2][105][15];
void solve()
cin>>n>>m>>k1>>k2;
// if(n*k2<m||m*k1<n)
//
// cout<<0<<endl;return;
//
dp[0][0][0][0]=dp[0][1][0][0]=1;
for(int i=1;i<=n+m;i++)
for(int t=0;t<以上是关于2020 icpc 上海站+模拟+dp的主要内容,如果未能解决你的问题,请参考以下文章
2021 ICPC上海 I.Steadily Growing Steam(dp)
2021 ICPC上海 G.Edge Groups(树形dp)
2021 ICPC上海 G.Edge Groups(树形dp)