【CF】Codeforces Round #423(Div.1)

来源:互联网 发布:淘宝授权书psd模板 编辑:程序博客网 时间:2024/06/03 14:44

原题地址

A. String Reconstruction

【题目大意】
给定n个字符串在一个串T中出现的位置,求构造一个符合要求的字典序最小的T。

【解题思路】
暴力赋值肯定会T,重点是如何维护空位置。
我们DSU维护每个位置右边第一个空位置在哪,剩下的填a就行了。
我们还可以用线段树完成这个东西。
我们也可以用一个数组记录这个东西。
我们还可以用一些玄学加暴力搞这个东西。

【代码】

#include<bits/stdc++.h>const int MAXN = 2e6+5;using namespace std;int n,maxL;char s[MAXN],ans[MAXN];int main() {//  freopen("A.in","r",stdin);//  freopen("A.out","w",stdout);    scanf("%d",&n);    while (n--)     {        int k;        scanf("%s%d",s,&k);        int len=strlen(s);        int p,start = 0;        for (int i=0;i<k;i++)         {            scanf("%d",&p);            start=max(start,p);            for (int j=start;j<p+len;j++)                ans[j]=s[j-p];            start=p+len;        }        maxL=max(maxL,p+len-1);    }    for(int i=1;i<=maxL;i++)     {        if (ans[i]) printf("%c",ans[i]);        else printf("a");    }    return 0;}

B. High Load

【题目大意】
将n个节点构造成一棵树,树上有k个出口,使得距离最远的两个出口距离最小,出口为所有度为1的点。

【解题思路】
显然可以想到这是一颗多叉(菊花)树。
那么我们就可以把这颗树分的尽量多叉。

【代码】

#include<bits/stdc++.h>using namespace std;int n,k;int main(){//  freopen("B.in","r",stdin);//  freopen("B.out","w",stdout);    scanf("%d%d",&n,&k);    n--;    int len1=n/k;    int len2=(n%k)?(len1+1):len1;    int ans=(n%k>=2)?(len2*2):(len1+len2);    printf("%d\n",ans);    for(int i=2;i<=k;i++)        printf("%d %d\n",1,i);    for(int i=k+1;i<=n+1;i++)        printf("%d %d\n",i-k,i);    return 0;}

C. DNA Evolution

【题目大意】
给定一个只包含A,T,C,G的字符串,有如下两种操作
1)修改一个点的字母
2)给定区间L, R和一个字符串e (strlen(e) <=10),组成一个足够区间长度的由若干个e重复组成的新串,eee…,问L,R区间中有几个位置对应的字母跟这个新的字符串对应的相同。

【解题思路】
e串长度最多为10,那么对于每一个字母最多有10种不同的起始位置,10个不同的长度进行重复。建立一个4*10*10的BST维护即可。

【代码】

#include<bits/stdc++.h>using namespace std;const int MAXN=1e5+10;int len,lena,ans,q;int id[305],tree[13][13][6][MAXN];char s[MAXN],a[13];inline int lowbit(int x){    return x&(-x);}inline void add(int x,int y,int z,int pos,int del){    while(pos<MAXN)    {        tree[x][y][z][pos]+=del;        pos+=lowbit(pos);    }}inline int query(int x,int y,int z,int pos){    int ret=0;    while(pos)    {        ret+=tree[x][y][z][pos];        pos-=lowbit(pos);    }    return ret;}int main(){    freopen("C.in","r",stdin);    freopen("C.out","w",stdout);    id['A']=0;id['T']=1;id['C']=2;id['G']=3;    scanf("%s",s);    len=strlen(s);    for(int i=1;i<=len;++i)        for(int j=1;j<=10;++j)            add(i%j,j,id[s[i-1]],i,1);    scanf("%d",&q);    while(q--)    {        int x,y,z;char ch[5];        scanf("%d",&x);        if(x==1)        {            scanf("%d%s",&y,ch);            for(int i=1;i<=10;++i)            {                add(y%i,i,id[s[y-1]],y,-1);                add(y%i,i,id[ch[0]],y,1);            }            s[y-1]=ch[0];        }        else        {            scanf("%d%d%s",&y,&z,a);            lena=strlen(a);ans=0;            for(int i=0;i<lena;++i)                ans+=query((y+i)%lena,lena,id[a[i]],z)-query((y+i)%lena,lena,id[a[i]],y-1);            printf("%d\n",ans);        }    }    return 0;}

D. Best Edge Weight

【题目大意】
给定一幅无向图,问每一条边的边权最大为多少,可以被包含在一颗这幅图的最小生成树内。

【解题思路】
显然要先求MST
求完之后,分两种情况讨论:

1.若一条边不在生成树上,这条边肯定与生成树上的边共同构成了一个环。如果我们想用这条边替代环上的一条边,则权值最大必须小于环上的边的最大权值。
这个用LCA+倍增简单维护一下就好了。

2.若一条边在生成树上,每个不在生成树上的边和生成树构成的环,可以先预处理出所有两端在u到v路径上的不在树上的边的最小值。它的权值一定要小于最小值。
这个还是可以倍增一下。

【代码】

#include<bits/stdc++.h>using namespace std;const int INF=1e9+10;const int MAXN=4e5+10;const int MAXP=20;int n,m,cnt;int fa[MAXN],ans[MAXN],dep[MAXN],head[MAXN],vs[MAXN];int mx[MAXP+5][MAXN],anc[MAXP+5][MAXN];bool bo[MAXN];struct Tway{     int u,v,w,nex,id; };Tway e[MAXN<<1],d[MAXN<<1];inline bool cmp(Tway a,Tway b){     return a.w<b.w;}inline int findf(int x){     return x==fa[x] ? x : fa[x]=findf(fa[x]);}inline void add(int u,int v,int w,int id){    ++cnt;    e[cnt].v=v;e[cnt].w=w;e[cnt].id=id;    e[cnt].nex=head[u];head[u]=cnt; }inline void dfs(int x,int f,int dis){    dep[x]=dep[f]+1;anc[0][x]=f;mx[0][x]=dis;    for(int i=1;i<=MAXP;++i)    {        anc[i][x]=anc[i-1][anc[i-1][x]];        mx[i][x]=max(mx[i-1][anc[i-1][x]],mx[i-1][x]);    }    for(int i=head[x];i;i=e[i].nex)        if(e[i].v!=f)        {            vs[e[i].v]=e[i].id;            dfs(e[i].v,x,e[i].w);        }}inline int lca( int x, int y, int &d ){    d=0;     if(dep[x]<dep[y] )         swap(x,y);    for(int i=MAXP;i>=0;--i)         if(dep[anc[i][x]]>=dep[y])         {            d=max(d,mx[i][x]);             x=anc[i][x];        }    if(x==y)         return x;    for(int i =MAXP;i>=0;--i)        if(anc[i][x]!=anc[i][y])        {            d=max(d,max(mx[i][x],mx[i][y]));            x=anc[i][x];y=anc[i][y];        }    d=max(d,max(mx[0][x],mx[0][y]));    return anc[0][x];}inline void solve(int x,int lca,int d){    x=findf(x);    while(dep[x]>dep[lca])    {        ans[vs[x]]=min(ans[vs[x]],d);        int y=findf(anc[0][x]);        fa[x]=y;        x=findf(x);    }}int main(){    freopen("D.in","r",stdin);    freopen("D.out","w",stdout);    scanf("%d%d",&n,&m);    for(int i=1;i<=n;++i)        fa[i]=i;    for(int i=1;i<=m;++i)    {        scanf("%d%d%d",&d[i].u,&d[i].v,&d[i].w);        d[i].id=i;    }    sort(d+1,d+m+1,cmp);    for(int i=1;i<=m;++i)    {        int fx=findf(d[i].u),fy=findf(d[i].v);        if(fx!=fy)        {            bo[i]=true;fa[fx]=fy;            add(d[i].u,d[i].v,d[i].w,d[i].id);            add(d[i].v,d[i].u,d[i].w,d[i].id);        }    }/*  for(int i=1;i<=n;++i)    {        printf("%d:",i);        for(int j=head[i];j;j=e[j].nex)            printf("%d ",e[j].v);        printf("\n");    }*/    dfs(1,0,0);    memset(ans,63,sizeof(ans));    for(int i=1;i<=n;++i)         fa[i]=i;    for(int i=1;i<=m;++i)        if(!bo[i])        {            int u=d[i].u,v=d[i].v,f=lca(u,v,ans[d[i].id]);            ans[d[i].id]--;            solve(u,f,d[i].w-1);             solve(v,f,d[i].w-1);        }    for(int i=1;i<=m;++i)         if(ans[i]>=INF)             printf("-1 ");        else             printf("%d ",ans[i]);    return 0;} 

E. Rusty String

【题目大意】
给定一个只包含通配符’?’和’v’,’K’的串,询问所有可能的循环节长度。

【解题思路】
首先个如果x是可能的循环节,那么2x,3x也一定是。
因此可以根据这个愉快地进行暴力
暴力check每一个长度,如果可行就把它的倍数都标为可行的。

当然正解是FFT。以下为看大佬博客整理

首先根据KMP的思想,如果存在长度为k的循环节那么存在长度为(n - k)的公共前后缀。
所以我们可以把这个串右移k位然后check,最后判一下特殊情况。

特殊情况是指类似于存在某一个i使得s[i] != s[i + 2k]并且s[i + k] == ‘?’。

首先可以初步地将一些循环节判断为不可行,对于看似没有问题的循环节长度,我们还需要check它的倍数中有没有被标记为不可行的,如果存在它就不可行(这样做的话就可以把以上的特殊情况处理掉)。

接下来是正常情况。

为了更快地进行check,所以,我们设A数组中A[i]为1当且仅当s[i] == ‘v’,B[i]为1当且仅当s[i] == ‘K’。

初步可行的条件是nk1i=0(A[i]×B[i+k])=0并且nk1i=0(B[i]×A[i+k])=0

然后为了能够顺利地进行下一步,我们设A’[i] = A[n - i - 1]。于是你会发现两边A’的下标和B的和是一个定值,而且范围不相交。因此我们可以把A’数组和B数组当成两个多项式的系数数组,然后进行FFT。

当然我不会FFT所以没写。

【代码】

#include<bits/stdc++.h>using namespace std;const int MAXN=5e5+5;int ans,len,T;char s[MAXN];bool flag[5],bo[MAXN];inline bool check(int l){    for(int i=0;i<l;++i)    {        char c=s[i];        for(int j=i+l;j<len;j+=l)        {            if(c!='?' && s[j]!='?' && c!=s[j])                return false;            if(s[j]!='?')                c=s[j];        }    }    return true;}int main(){    freopen("E.in","r",stdin);    freopen("E.out","w",stdout);    scanf("%d",&T);    while(T--)    {        scanf("%d%s",&len,s);        flag[0]=flag[1]=false;        for(int i=0;i<len;++i)        {            if(s[i]=='V')                flag[0]=true;            if(s[i]=='K')                flag[1]=true;        }        if(!flag[0] && !flag[1])        {            printf("%d\n",len);            for(int i=1;i<=len;++i)                printf("%d ",i);            printf("\n");            continue;        }        ans=0;        for(int i=1;i<=len;++i)            if(!bo[i] && check(i))                for(int j=i;j<=len;j+=i)                {                    ans+=bo[j]?0:1;                    bo[j]=true;                }        printf("%d\n",ans);        for(int i=1;i<=len;++i)            if(bo[i])            {                printf("%d ",i);                bo[i]=false;            }        printf("\n");    }    return 0;}

F. Dirty Arkady’s Kitchen

【题目大意】
给一个无向图,其中的无向边有一些限定可以通行时间,人一开始在1号点,每一时刻他都需要不断移动,通过一条无向边的时间是1,问最早能在什么时刻到达n号点

【解题思路】
最简单的dp暴力是对于每一个点记录每个时间能否走到,然后转移,这样由于每条边进行了多次转移,显然会TLE。

观察到如果在第i秒来到点S,则第i+2秒也可以来到S,以此类推(当然不能超过范围)。那么可以考虑从奇偶性入手进行dp。

所以我们可以把奇偶性相同的一些时间点一起更新,将点分裂成奇数时刻和偶数时刻的点,把边拆成4条有向边:在偶数u->奇数v,奇数u->偶数v,v->u同理 。

更新的时候一次性把这条边能更新到的全部时间点都更新,就能使得边的更新数为m条 。

此时显然不能枚举时间去计算边的贡献,我们按照边产生贡献的时间顺序去计算贡献 。

为了更新每条边能贡献到的所有时刻,对于每条边需要求出一个dp[i]表示第一次到达这条边的时刻 。

对于一个点的所有能产生贡献的出边,他们产生贡献的顺序一定是按照出现时间升序的,对于每个点的出边按出现时刻升序排序。对每个点维护他的出现时间段l[x] r[x]

那么我们可以:一开始从1的偶数点的第一条出边开始,每次判断当前这条边是否能产生贡献,尝试用它的dpi去更新这条边的结束点的时间段。

如果成功更新且结束点不在队列里,将结束点和他的当前弧放进队列,权为(u,v)出现后结束点当前弧出现的最早时间。

每次当前出发点的当前弧成功产生贡献或已经消失后,更新他的当前弧,将点和他新的当前弧放进队列,权也为这条边能够出现的最早时间。

【代码】

#include<bits/stdc++.h>using namespace std;const int MAXN=5e5+5;int n,m,ans;int head[MAXN][2];struct Tnode{    int v,l,r;    Tnode(){}    Tnode(int vv,int ll,int rr)    {        v=vv;l=ll;r=rr;    }    friend bool operator <(Tnode A,Tnode B)    {        return A.l<B.l;    }};vector<Tnode>way[MAXN][2];struct Tp{    int x,y;    Tp(){}    Tp(int xx,int yy)    {        x=xx;y=yy;    }    friend bool operator <(Tp A,Tp B)    {        return A.y>B.y;    }};priority_queue<Tp>q;void init(){    scanf("%d%d",&n,&m);    for(int i=1;i<=m;++i)    {        int u,v,l,r;        scanf("%d%d%d%d",&u,&v,&l,&r);        for(int j=0;j<2;++j)        {            way[u][j].push_back(Tnode(v,l,r));            way[v][j].push_back(Tnode(u,l,r));        }    }    for(int i=1;i<=n;++i)        for(int j=0;j<2;++j)            sort(way[i][j].begin(),way[i][j].end());}int dij(){    int now,tim,xx,yy;    q.push(Tp(1,0));    while(!q.empty())    {        xx=q.top().x;yy=now=q.top().y;tim=yy&1;        q.pop();        if(xx==n)            return yy;        for(int j=head[xx][tim];j<way[xx][tim].size();++j)        {            Tnode w=way[xx][tim][j];            if(w.l>now)                break;            head[xx][tim]=j+1;            if(w.r<yy)                continue;            now=max(now,w.r);            if(now-tim&1)                --now;            int tmp=max(w.l,yy);            if(tmp%2 != tim)//first time: (tmp&1 != tim)                ++tmp;            if(tmp+1<=w.r && tmp>=w.l)                q.push(Tp(w.v,tmp+1));        }    }    return -1;}int main(){    freopen("F.in","r",stdin);    freopen("F.out","w",stdout);    init();    printf("%d\n",dij());    return 0;}
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机膜进空气了怎么办 铁的下水管漏水怎么办 刚浇的水泥开裂怎么办 马桶移位器堵了怎么办 分散片直接吃了怎么办 坐马桶拉不出来怎么办 泥状面膜干了怎么办 苹果6s掉水里了怎么办 手机充电口松了怎么办 手机液体膜碎了怎么办 车膜贴的太黑了怎么办 苹果7后壳氧化怎么办 苹果6s后盖氧化怎么办 苹果6s后壳氧化怎么办 食道癌吃了就吐怎么办 有眼袋和泪沟怎么办? 泪沟和眼袋都有怎么办 脸上的汗毛很长怎么办 点痣留下褐色印怎么办 氮氧传感器坏了怎么办 考试车离合太松怎么办 胎心监护不过关怎么办 羚羊角的功效与作用发烧怎么办 小孩上课注意力不集中怎么办 2岁宝宝不会说话怎么办 小孩脖子上长淋巴结怎么办 小孩子上课注意力不集中该怎么办 脸过敏干燥起皮怎么办 脸上皮肤干燥起皮怎么办 身上皮肤干燥起皮怎么办 皮肤暗黄有色斑怎么办 我皮肤干燥暗黄怎么办 脸上有皮肤暗黄怎么办 皮肤暗黄毛孔大怎么办 脸上很干燥起皮怎么办 脸上的皮肤起皮怎么办 身体的皮肤好干怎么办 滴油雾化器炸油怎么办 已经发炎的痘痘怎么办 被养生馆骗了怎么办 做完微针结痂了怎么办