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)

ICPC20上海C——小白也能秒懂的数位dp讲解

2021 ICPC上海 G.Edge Groups(树形dp)

2021 ICPC上海 G.Edge Groups(树形dp)

2021 ICPC上海 G.Edge Groups(树形dp)

2021 ICPC上海 I.Steadily Growing Steam(dp)