Codeforces Round 764 (Div. 3)E G

E. Masha-forgetful

题意
给定一个长度为m的字符串s,再给定n个字符串,要求将s分割成长度大于等于2的一些子串,且这些子串在给定的n个字符串中,问应该如何分割,答案输出分割出来的每一段在n个字符串中的位置。
思路
任何字符串都可以用长度为2,3的子串拼起来,因此我们记录下来给定的n个字符串中所有长度为2,3的子串,这里有一个小技巧,记录长度为2,3的子串时,我们可以像哈希一样将其看成一个10进制数,然后就可以用一个数来表示一个子串,然后我们用 d p [ i ] dp[i] dp[i]表示从第0至第i位是否已经找到合适的子串。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;
using tp=tuple;//用tuple代替结构体

typedef long long ll;
typedef pair pii;

const int N = 1e5 + 10;

void solve()
{
    int n,m;cin>>n>>m;
    mapmp[2];//用map存下来长度为2和3,其子串的权值的位置
    for(int i=1;i<=n;i++)
    {
        string s;cin>>s;s=" "+s;
        for(int j=1;j<=m-1;j++)
        {
            int p=(s[j]-'0')*10+(s[j+1]-'0');//小技巧,用数字表示子串,跟哈希一样的思想
            mp[0][p]={j,j+1,i};

            if(j<=m-2)//记录长度为3的子串
            {
                int p=(s[j]-'0')*100+(s[j+1]-'0')*10+(s[j+2]-'0');
                mp[1][p]={j,j+2,i};
            }
        }
    }

    string s;cin>>s;s=" "+s;

    vectordp(m+1,0),pre(m+1);//pre记录以该位置结尾的子串是被分割成长度为2还是3的子串
    dp[0]=1;//dp边界初始化
    for(int i=2;i<=m;i++)
    {
        int p1=(s[i-1]-'0')*10+(s[i]-'0');
        if(dp[i-2]&&mp[0].count(p1))
        {
            dp[i]=1;
            pre[i]=2;
        }

        if(i>=3)
        {
            int p2=(s[i-2]-'0')*100+(s[i-1]-'0')*10+(s[i]-'0');
            if(dp[i-3]&&mp[1].count(p2))
            {
                dp[i]=1;
                pre[i]=3;
            }
        }
    }

    if(dp[m])//从后往前回溯,记录方案
    {
        vectorans;
        for(int i=m;i>=1;i-=pre[i])
        {
            if(pre[i]==2)
            {
                int p1=(s[i-1]-'0')*10+(s[i]-'0');
                ans.push_back(mp[0][p1]);
            }
            else
            {
                int p2=(s[i-2]-'0')*100+(s[i-1]-'0')*10+(s[i]-'0');
                ans.push_back(mp[1][p2]);
            }
        }
        reverse(ans.begin(),ans.end());
        cout<>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

G. MinOr Tree

题意
给定n个点,m条边,求所有权值按位或的最小生成树。

思路
我们可以从高位向低位枚举,去掉该位为1的边后,图是否能连通,如果能连通,那么这一位就不在答案中,否则这一位就在答案中
判断连通可以用并查集

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long ll;
typedef pair pii;

const int N = 1e5 + 10;

struct edge
{
    int u,v,w;
};
int n,m;
vectoredg;
vectorst;
vectorf;

int find(int x)
{
    if(x!=f[x])f[x]=find(f[x]);
    return f[x];
}
bool check(int x)
{
    f.resize(n+1);
    for(int i=1;i<=n;i++)f[i]=i;

    for(int i=1;i<=m;i++)
    {
        if(!st[i]||edg[i].w>>x&1)continue;

        int xx=find(edg[i].u),yy=find(edg[i].v);
        if(xx!=yy)f[xx]=yy;
    }

    for(int i=1;i<=n;i++)
        if(find(f[i])!=find(1))return false;
    return true;
}
void solve()
{
    cin>>n>>m;
    edg.resize(m+1);
    st.resize(m+1);

    for(int i=1;i<=m;i++)
    {
        int u,v,w;cin>>u>>v>>w;
        edg[i]={u,v,w};
        st[i]=1;
    }

    int ans=0;
    for(int i=30;i>=0;i--)
    {
        if(check(i))//判断少了含这一位的边后图是否连通
        {
            for(int j=1;j<=m;j++)
                if(edg[j].w>>i&1)st[j]=0;
        }
        else ans+=(1<>T;
    while(T--)
    {
        solve();
    }
    return 0;
}

你可能感兴趣的:(杂题,算法,数据结构,c++)