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<2;t++)
        {
            if(t==0)
            {
                for(int j=1;j<=min(i,n);j++)
                {
                    for(int k=1;k<=min(k1,j);k++)
                    {   // 前i个数中1的数目为j个 其中有k个连续的1
                        dp[i][t][j][k]=(dp[i][t][j][k]+dp[i-1][t][j-1][k-1])%mod;
                        dp[i][!t][i-j][1]=(dp[i][!t][i-j][1]+dp[i-1][t][j][k])%mod;
                    }
                }
            }
            else
            {
                for(int j=1;j<=min(i,m);j++)
                {
                    for(int k=1;k<=min(k2,j);k++)
                    {
                        dp[i][t][j][k]=(dp[i][t][j][k]+dp[i-1][t][j-1][k-1])%mod;
                        dp[i][!t][i-j][1]=(dp[i][!t][i-j][1]+dp[i-1][t][j][k])%mod;
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=k1;i++) ans=(ans+dp[n+m][0][n][i])%mod;
    for(int i=1;i<=k2;i++) ans=(ans+dp[n+m][1][m][i])%mod;
    cout<<ans<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值