1. 程式人生 > 其它 >Codeforces Round #423 (Div. 1, rated, based on VK Cup Finals)

Codeforces Round #423 (Div. 1, rated, based on VK Cup Finals)

CF827A String Reconstruction

洛谷傳送門
CF827A


分析

考慮維護每個左端點通過一個區間覆蓋到的最大右端點,然後直接雙指標鋪一下就可以了


程式碼

#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int N=1000011;
char s[N]; int mx,m,len[N],r[N],L[N];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	m=iut();
	for (int i=1;i<=m;++i){
		scanf("%s",s+r[i-1]+1);
		len[i]=strlen(s+r[i-1]+1);
		r[i]=r[i-1]+len[i];
		for (int j=iut();j;--j){
			int x=iut();
			if (mx<x+len[i]-1) mx=x+len[i]-1;
			if (len[L[x]]<=len[i]) L[x]=i;
		}
	}
	for (int i=1,j=0,t;i<=mx;++i){
		if (L[i]&&j<i+len[L[i]]-1) j=i+len[L[i]]-1,t=L[i];
		if (j<i) putchar(97);
		    else putchar(s[r[t]+i-j]);
	}
	return 0;
}

CF827B High Load

洛谷傳送門
CF827B


分析

類似於菊花圖直接構造出長度相近的 \(k\) 條鏈,這樣一定是最優的


程式碼

#include <iostream>
using namespace std;
int n,m,ans;
int main(){
	ios::sync_with_stdio(0);
	cin>>n>>m,ans=(n-1+m-1)/m*2;
	if ((n-1)%m==1) --ans;
	cout<<ans<<endl;
	for (int i=1;i<=m;++i) cout<<"1 "<<i+1<<endl;
	for (int i=m+2;i<=n;++i) cout<<i-m<<" "<<i<<endl;
	return 0;
}

CF827C DNA Evolution

洛谷傳送門
CF827C


分析

因為長度只有十,所以根據模數和餘數直接開 \(\frac{10*(10-1)}{2}=45\) 棵線段樹維護區間字母個數即可

理論上的空間為 \(O((n+m)\log n)\)


程式碼

#include <cstdio>
#include <cctype>
#include <cstring>
using namespace std;
const int N=100011; char s[N];
int w[N<<6][4],ls[N<<6],rs[N<<6],a[N],n,cnt,rt[11][11];
int iut(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
int rk(char ch){
	if (ch=='A') return 0;
	else if (ch=='T') return 1;
	else if (ch=='G') return 2;
	else return 3; 
}
void update(int &rt,int l,int r,int x,int y,int z){
	if (!rt) rt=++cnt; w[rt][y]+=z;
	if (l==r) return;
	int mid=(l+r)>>1;
	if (x<=mid) update(ls[rt],l,mid,x,y,z);
	    else update(rs[rt],mid+1,r,x,y,z);
}
int query(int rt,int l,int r,int x,int y,int z){
	if (!rt) return 0;
	if (l==x&&r==y) return w[rt][z];
	int mid=(l+r)>>1;
	if (y<=mid) return query(ls[rt],l,mid,x,y,z);
	else if (x>mid) return query(rs[rt],mid+1,r,x,y,z);
	    else return query(ls[rt],l,mid,x,mid,z)+query(rs[rt],mid+1,r,mid+1,y,z); 
}
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for (int i=1;i<=n;++i){
		a[i]=rk(s[i]);
		for (int j=1;j<=10;++j)
		    update(rt[j][i%j],1,n,i,a[i],1);
	}
	for (int T=iut();T;--T){
		int opt=iut();
		if (opt==1){
			int x=iut(),y=rk(getchar());
			for (int j=1;j<=10;++j){
				update(rt[j][x%j],1,n,x,a[x],-1);
				update(rt[j][x%j],1,n,x,y,1);
			}
			a[x]=y;
		}else{
			int l=iut(),r=iut(),len,ans=0;
			scanf("%s",s+1),len=strlen(s+1);
			if (len>r-l+1) len=r-l+1;
			for (int j=1;j<=len;++j)
				ans+=query(rt[len][(l+j-1)%len],1,n,l,r,rk(s[j]));
			print(ans),putchar(10);
		}
	}
	return 0;
}

CF827D Best Edge Weight

洛谷傳送門
CF827D


分析

先建出一棵最小生成樹,觀察到非樹邊的最大邊權就是樹上路徑最大邊權減一,這個可以用倍增(ST表)來維護。
然後樹邊的最大邊權就是所有經過它的非樹邊的最小邊權減一,如果沒有非樹邊就是-1,這個可以用逆ST表來維護。


程式碼

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=200011; struct node{int y,rk,next;}e[N<<1]; struct rec{int x,y,w;}a[N];
int f[N][18],g[N][18],dp[N][18],F[N],dep[N],et=1,ans[N],rk[N],n,m,as[N],cho[N]; bool tree[N];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(int ans){
	if (ans<0) ans=-ans,putchar('-');
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
void Min(int &x,int y){x=x<y?x:y;}
int getf(int u){return F[u]==u?u:F[u]=getf(F[u]);}
bool cmp(int x,int y){return a[x].w<a[y].w;}
int Get(int x,int y){return a[x].w>a[y].w?x:y;}
void dfs(int x,int fa){
    for (int I=0;I<17&&f[x][I];++I)
	    f[x][I+1]=f[f[x][I]][I],
		    g[x][I+1]=Get(g[x][I],g[f[x][I]][I]); 
	for (int i=as[x];i;i=e[i].next)
	if (e[i].y!=fa){
		f[e[i].y][0]=x,g[e[i].y][0]=e[i].rk;
		dep[e[i].y]=dep[x]+1,dfs(e[i].y,x);
	}
}
int lca(int x,int y){
	if (dep[x]<dep[y]) x^=y,y^=x,x^=y;
	for (int z=dep[x]-dep[y];z;z&=z-1) x=f[x][cho[-z&z]];
	if (x==y) return x;
	for (int i=17;~i;--i)
	if (f[x][i]!=f[y][i])
	    x=f[x][i],y=f[y][i];
	return f[x][0];
}
int main(){
	n=iut(),m=iut();
	for (int i=0;i<18;++i) cho[1<<i]=i;
	for (int i=1;i<=n;++i) F[i]=i;
	for (int i=1;i<=m;++i) a[i]=(rec){iut(),iut(),iut()},rk[i]=i;
	sort(rk+1,rk+1+m,cmp);
	for (int i=1;i<=m;++i){
		rec t=a[rk[i]]; int fa=getf(t.x),fb=getf(t.y);
		if (fa!=fb){
			F[fa]=fb,tree[rk[i]]=1;
			e[++et]=(node){t.y,rk[i],as[t.x]},as[t.x]=et;
			e[++et]=(node){t.x,rk[i],as[t.y]},as[t.y]=et;
		}
	}
	dfs(1,0);
	memset(dp,0x3f,sizeof(dp));
	for (int i=1;i<=m;++i)
	if (!tree[i]){
		int X=a[i].x,Y=a[i].y,Lca=lca(X,Y);
		for (int z=dep[X]-dep[Lca];z;z&=z-1){
			Min(dp[X][cho[-z&z]],a[i].w);
			ans[i]=Get(ans[i],g[X][cho[-z&z]]),X=f[X][cho[-z&z]];
		}
		for (int z=dep[Y]-dep[Lca];z;z&=z-1){
			Min(dp[Y][cho[-z&z]],a[i].w);
			ans[i]=Get(ans[i],g[Y][cho[-z&z]]),Y=f[Y][cho[-z&z]];
		}
		ans[i]=a[ans[i]].w;
	}
	for (int j=17;j;--j)
	for (int i=1;i<=n;++i)
	if (dp[i][j]!=dp[0][0])
		Min(dp[f[i][j-1]][j-1],dp[i][j]),
		    Min(dp[i][j-1],dp[i][j]);
	for (int i=2;i<=n;++i)
	    if (dp[i][0]!=dp[0][0]) ans[g[i][0]]=dp[i][0];
	for (int i=1;i<=m;++i) print(ans[i]-1),putchar(i==n?10:32);
	return 0;
}

CF827E Rusty String

洛谷傳送門
CF827E


分析

往右移動 \(d\) 步重合那麼它一定有一個長度為 \(n-1-d\) 的Border(萬用字元)

不如先用萬用字元的方法,那就是 \(S[i]\)\(S[n-x+i]\) 匹配,

把後面這個字串反轉成 \(S'[n-1-(n-x+i)]\) 也就是 \(S'[x-1+i]\)

多項式乘法後的項被放在了 \(x-1\),匹配函式就是 \((S[i]-S[j])^2S[i]S[j]\)

直接三次多項式乘法就可以了,這裡寫的是NTT

然後發現樣例都過不了,是因為上面的是充分不必要條件,因為未知字元並不是萬用字元,

所以如果 \(kd\) 的 Border不對,那麼 \(d\) 的Border也不對,再 \(O(n\log n)\) 判斷一下就可以了


程式碼

#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <algorithm>
#define mem(f,n) memset(f,0,sizeof(int)*(n))
#define cpy(f,g,n) memcpy(f,g,sizeof(int)*(n))
using namespace std;
const int mod=998244353,i3=(mod+1)/3,i2=(mod+1)/2,N=500011;
typedef long long lll; typedef unsigned long long ull;
int n,Gmi[31],Imi[31],a[N],b[N],Ans[N],period[N],len,ff[N<<2],gg[N<<2],tt[N<<2];
int iut(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
int ksm(int x,int y){
	int ans=1;
	for (;y;y>>=1,x=1ll*x*x%mod)
	    if (y&1) ans=1ll*ans*x%mod;
	return ans;
}
namespace Theoretic{
	int rev[N<<2],LAST; ull Wt[N<<2],F[N<<2];
	void Pro(int n){
		if (LAST==n) return; LAST=n,Wt[0]=1;
		for (int i=0;i<n;++i)
		    rev[i]=(rev[i>>1]>>1)|((i&1)?n>>1:0);
	}
	void NTT(int *f,int n,int op){
		Pro(n);
		for (int i=0;i<n;++i) F[i]=f[rev[i]];
	    for (int o=1,len=1;len<n;++o,len<<=1){
	    	int W=(op==1)?Gmi[o]:Imi[o];
	    	for (int j=1;j<len;++j) Wt[j]=Wt[j-1]*W%mod;
	    	for (int i=0;i<n;i+=len+len)
	    	for (int j=0;j<len;++j){
	    		int t=Wt[j]*F[i|j|len]%mod;
	    		F[i|j|len]=F[i|j]+mod-t,F[i|j]+=t;
			}
	    	if (o==10) for (int j=0;j<n;++j) F[j]%=mod;
		}
		if (op==-1){
			int invn=ksm(n,mod-2);
			for (int i=0;i<n;++i) F[i]=F[i]%mod*invn%mod;
		}else for (int i=0;i<n;++i) F[i]%=mod;
		for (int i=0;i<n;++i) f[i]=F[i];
    }
	void Cb(int *f,int *g,int n){
		for (int i=0;i<n;++i) f[i]=1ll*f[i]*g[i]%mod;
	}
}
void GmiImi(){
	for (int i=0;i<31;++i) Gmi[i]=ksm(3,(mod-1)/(1<<i));
	for (int i=0;i<31;++i) Imi[i]=ksm(i3,(mod-1)/(1<<i));		
}
int main(){
    GmiImi();
    for (int T=iut();T;--T){
    	n=iut(),Ans[0]=0;
    	for (int i=0;i<n;++i){
    		char ch=getchar();
    		while (!isalpha(ch)&&ch!='?') ch=getchar();
    		b[n-i-1]=a[i]=(ch=='?')?0:(ch=='V'?1:2);
		}
		for (len=1;len<n*2;len<<=1);
		for (int i=0;i<n;++i) ff[i]=a[i]*a[i]*a[i];
		for (int i=0;i<n;++i) gg[i]=b[i];
	    Theoretic::NTT(ff,len,1),Theoretic::NTT(gg,len,1),Theoretic::Cb(ff,gg,len);
		for (int i=0;i<len;++i) tt[i]=(tt[i]+ff[i])%mod;
		mem(ff+n,len-n),mem(gg+n,len-n);
		for (int i=0;i<n;++i) ff[i]=a[i];
		for (int i=0;i<n;++i) gg[i]=b[i]*b[i]*b[i];
	    Theoretic::NTT(ff,len,1),Theoretic::NTT(gg,len,1),Theoretic::Cb(ff,gg,len);
		for (int i=0;i<len;++i) tt[i]=(tt[i]+ff[i])%mod;
		mem(ff+n,len-n),mem(gg+n,len-n);
		for (int i=0;i<n;++i) ff[i]=a[i]*a[i];
		for (int i=0;i<n;++i) gg[i]=b[i]*b[i];
	    Theoretic::NTT(ff,len,1),Theoretic::NTT(gg,len,1),Theoretic::Cb(ff,gg,len);
		for (int i=0;i<len;++i) tt[i]=(tt[i]-2ll*ff[i]+mod*2)%mod;
		mem(ff,len),mem(gg,len),Theoretic::NTT(tt,len,-1);
		for (int i=1;i<=n;++i)
		    if (!tt[n-1-i]) period[i]=1;
		for (int i=1;i<=n;++i){
			for (int j=i+i;j<=n&&period[i];j+=i)
		        if (!period[j]) period[i]=0;
		    if (period[i]) Ans[++Ans[0]]=i,period[i]=0;
		}
		print(Ans[0]),putchar(10),mem(tt,len);
		for (int i=1;i<=Ans[0];++i)
		    print(Ans[i]),putchar(i==Ans[0]?10:32);
	}
    return 0;
}

CF827F Dirty Arkady's Kitchen

洛谷傳送門
CF827F


分析

無向邊的話,人可以一直在這條邊上反覆橫跳,那麼一個點理應要被拆成奇點和偶點。

\(dis[x][0/1]\) 表示從起點走到 \(x\) 的步數為偶數/奇數步時的最晚時間

然後將無向邊拆成奇數特供和偶數特供,把它們丟進一個按照最早通過時間排序

如果當前的最晚時間不能滿足的話,就先把它丟進一個延遲放入的數組裡,等到有其它邊啟動時再將這條邊啟動。

這樣小根堆裡最多隻會有 \(O(m)\) 條邊,所以時間複雜度為 \(O(m\log m)\)


程式碼

#include <cstdio>
#include <cctype>
#include <queue>
#include <cstring>
using namespace std;
const int N=500011;
struct rec{
	int x,y,l,r;
	bool operator <(const rec &t)const{
	     return l>t.l;
	}
};
vector<rec>e[N][2];
priority_queue<rec>q;
int n,m,dis[N][2];
int iut(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int max(int a,int b){return a>b?a:b;}
int main(){
	n=iut(),m=iut();
	if (n==1) return !printf("0");
	for (int i=1;i<=m;++i){
		int x=iut(),y=iut(),l=iut(),r=iut()-1,opt=(r-l)&1;
		if (l<r){
			q.push((rec){x,y,l+1,r-(!opt)});
			q.push((rec){y,x,l+1,r-(!opt)});
		}
		q.push((rec){x,y,l,r-opt}),q.push((rec){y,x,l,r-opt});
	}
	memset(dis,0xcf,sizeof(dis)),dis[1][0]=0;
	while (!q.empty()){
		rec t=q.top(); int opt=t.l&1; q.pop();
		if (dis[t.x][opt]>=t.l){
			if (t.y==n) return !printf("%d",t.l+1);
			if (dis[t.y][opt^1]<=t.r){
				dis[t.y][opt^1]=t.r+1;				
				int len=e[t.y][opt^1].size();
				for (int i=0;i<len;++i){
					rec _t=e[t.y][opt^1][i];
					if (t.l+1<=_t.r) _t.l=t.l+1,q.push(_t);
				}
				e[t.y][opt^1].clear();
			}
		}else e[t.x][opt].push_back(t);
	}
	return !printf("-1");
}