【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’。
初步可行的条件是
然后为了能够顺利地进行下一步,我们设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,问最早能在什么时刻到达
【解题思路】
最简单的dp暴力是对于每一个点记录每个时间能否走到,然后转移,这样由于每条边进行了多次转移,显然会TLE。
观察到如果在第
所以我们可以把奇偶性相同的一些时间点一起更新,将点分裂成奇数时刻和偶数时刻的点,把边拆成4条有向边:在偶数u->奇数v,奇数u->偶数v,v->u同理 。
更新的时候一次性把这条边能更新到的全部时间点都更新,就能使得边的更新数为m条 。
此时显然不能枚举时间去计算边的贡献,我们按照边产生贡献的时间顺序去计算贡献 。
为了更新每条边能贡献到的所有时刻,对于每条边需要求出一个
对于一个点的所有能产生贡献的出边,他们产生贡献的顺序一定是按照出现时间升序的,对于每个点的出边按出现时刻升序排序。对每个点维护他的出现时间段
那么我们可以:一开始从1的偶数点的第一条出边开始,每次判断当前这条边是否能产生贡献,尝试用它的
如果成功更新且结束点不在队列里,将结束点和他的当前弧放进队列,权为
每次当前出发点的当前弧成功产生贡献或已经消失后,更新他的当前弧,将点和他新的当前弧放进队列,权也为这条边能够出现的最早时间。
【代码】
#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;}
- 【CF】Codeforces Round #423(Div.1)
- 【CF】Codeforces Round #301 (Div. 2) ABCDE
- 【CF】Codeforces Round #361 (Div. 2)
- CF-Codeforces Round #377 (Div. 2)
- [CF刷题]Codeforces Round #443 (Div. 2)
- CF Codeforces Round #257 (Div. 1) A (449A) Jzzhu and Chocolate
- CF-Codeforces Round #210 (Div. 1)-A-Levko and Array Recovery
- CF Codeforces Round #258 (Div. 2) B (451B)
- 【CF】Codeforces Round #124 (Div. 2) 197A Plate Game
- CF-Codeforces Round #392 (Div. 2)-A-Holiday Of Equality
- CF-Codeforces Round #392 (Div. 2)-B-Blown Garland
- CF-Codeforces Round #410 (Div. 2)-A~C
- CF-Codeforces Round #410 (Div. 2)-D-Mike and distribution
- CF-Codeforces Round #420 (Div. 2)-D-Okabe and City
- CF-Codeforces Round #420 (Div. 2)-C-Okabe and Boxes
- CF-Codeforces Round #420 (Div. 2) A ~ E
- CF——Codeforces Round #428 (Div. 2)C. Journey
- Codeforces Round #423(Div.2)
- 运维新手 | 如何快速轻松记住 Linux 命令?
- 前端能力学习进度
- react里使用less时antd报错
- Code Control MVC框架-保存和加载模型
- Trie树
- 【CF】Codeforces Round #423(Div.1)
- Resource interpreted as Stylesheet but transferred with MIME type application/octet-stream
- Lamda表达式,从抵制到喜欢
- 关于协方差矩阵
- 中国IT风险投资机构
- VTK交互-观察者/命令模式
- Git合并分支(下)
- 牛客练习赛8,给个n,求1到n的所有数的约数个数的和~
- 人到了32岁就有自己的工作方法了吗