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


CF827A String Reconstruction

洛谷传送门
CF827A


分析

考虑维护每个左端点通过一个区间覆盖到的最大右端点,然后直接双指针铺一下就可以了


代码

#include 
#include 
#include 
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

CF827B High Load

洛谷传送门
CF827B


分析

类似于菊花图直接构造出长度相近的 \(k\) 条链,这样一定是最优的


代码

#include 
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<

CF827C DNA Evolution

洛谷传送门
CF827C


分析

因为长度只有十,所以根据模数和余数直接开 \(\frac{10*(10-1)}{2}=45\) 棵线段树维护区间字母个数即可

理论上的空间为 \(O((n+m)\log n)\)


代码

#include 
#include 
#include 
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 
#include 
#include 
#include 
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=xa[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]

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 
#include 
#include 
#include 
#include 
#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>1]>>1)|((i&1)?n>>1:0);
	}
	void NTT(int *f,int n,int op){
		Pro(n);
		for (int i=0;i

CF827F Dirty Arkady's Kitchen

洛谷传送门
CF827F


分析

无向边的话,人可以一直在这条边上反复横跳,那么一个点理应要被拆成奇点和偶点。

\(dis[x][0/1]\) 表示从起点走到 \(x\) 的步数为偶数/奇数步时的最晚时间

然后将无向边拆成奇数特供和偶数特供,把它们丢进一个按照最早通过时间排序

如果当前的最晚时间不能满足的话,就先把它丢进一个延迟放入的数组里,等到有其它边启动时再将这条边启动。

这样小根堆里最多只会有 \(O(m)\) 条边,所以时间复杂度为 \(O(m\log m)\)


代码

#include 
#include 
#include 
#include 
using namespace std;
const int N=500011;
struct rec{
	int x,y,l,r;
	bool operator <(const rec &t)const{
	     return l>t.l;
	}
};
vectore[N][2];
priority_queueq;
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=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

相关