Codeforces Round #612 (Div. 1+Div. 2)

A

找一个除去开头外的最长连续\(P\)段即可。

#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int n;
char s[100100];


int main()
{
	int T=read();
	while (T--)
	{
		n=read();
		scanf("%s",s+1);s[++n]='A';
		int ans=0,lst=0;
		rep(i,1,n)
		{
			if (s[i]=='A')
			{
				if (lst) ans=max(ans,i-lst-1);
				lst=i;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

B

枚举前两个串,第三个串是什么就确定下来了,直接用map查即可。
(貌似这么写有点卡常)

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

int n,m,num[500],sum=0;
string s[2020];
map<string,int> mp;

bool diff(string a,string b)
{
	rep(i,0,m-1)
		if (a[i]==b[i]) return 0;
	return 1;
}

int main()
{
	n=read();m=read();
	sum='S'+'E'+'T';
	rep(i,1,n) {cin >> s[i];mp[s[i]]++;}
	int ans=0;
	rep(i,1,n)
	{
		rep(j,i+1,n)
		{
			string a="";
			rep(k,0,m-1)
			{
				if (s[i][k]==s[j][k]) a=a+s[i][k];
				else a+=(sum-s[i][k]-s[j][k]);
			}
			if (mp.count(a)) ans+=mp[a];
		}
	}
	cout << ans/3;
	return 0;
}

C

想了一万年咋贪心,最后发现dp就完事了。

\(f_{i,j,0/1}\)表示前\(i\)个数中使用了\(j\)个奇数,第\(i\)是偶数/奇数时的最大complexity. 直接转移即可。

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

namespace My_Math{
	#define N 100000

	int fac[N+100],invfac[N+100];

	int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
	int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
	int mul(int x,int y) {return 1ll*x*y%maxd;}
	ll qpow(ll x,int y)
	{
		ll ans=1;
		while (y)
		{
			if (y&1) ans=mul(ans,x);
			x=mul(x,x);y>>=1;
		}
		return ans;
	}
	int getinv(int x) {return qpow(x,maxd-2);}

	int C(int n,int m)
	{
		if ((n<m) || (n<0) || (m<0)) return 0;
		return mul(mul(fac[n],invfac[m]),invfac[n-m]);
	}

	void math_init()
	{
		fac[0]=invfac[0]=1;
		rep(i,1,N) fac[i]=mul(fac[i-1],i);
		invfac[N]=getinv(fac[N]);
		per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
	}
	#undef N
}
using namespace My_Math;

int n,a[110],f[110][110][2];

int main()
{
	n=read();
	rep(i,1,n) a[i]=read();
	memset(f,0x3f,sizeof(f));
	f[0][0][0]=f[0][0][1]=0;
	rep(i,1,n) rep(j,0,i)
	{
		if ((j) && ((a[i]&1) || (!a[i])))
		{
			f[i][j][1]=min(f[i][j][1],min(f[i-1][j-1][0]+1,f[i-1][j-1][1]));
		}
		if ((!(a[i]&1)) || (!a[i]))
		{
			f[i][j][0]=min(f[i][j][0],min(f[i-1][j][0],f[i-1][j][1]+1));
		}
	}
	int ans=min(f[n][(n+1)>>1][0],f[n][(n+1)>>1][1]);
	printf("%d",ans);
	return 0;
}

D

无解的情况比较显然:当子树\(i\)中的点的个数\(<c_i\)时无解。

接下来将通过构造指出存在一种所用值为\([1,n]\)的排列的构造方法。

dfs整棵树,对当前点\(u\)找到排列\([1,n]\)中尚未使用的第\(c_u+1\)小的数,并将其赋给点\(u\). 之后再遍历它的子树。这样的话就能保住前\(c_u\)个数正好分配给\(u\)的子树中比\(u\)上数小的点。

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

namespace My_Math{
	#define N 100000

	int fac[N+100],invfac[N+100];

	int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
	int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
	int mul(int x,int y) {return 1ll*x*y%maxd;}
	ll qpow(ll x,int y)
	{
		ll ans=1;
		while (y)
		{
			if (y&1) ans=mul(ans,x);
			x=mul(x,x);y>>=1;
		}
		return ans;
	}
	int getinv(int x) {return qpow(x,maxd-2);}

	int C(int n,int m)
	{
		if ((n<m) || (n<0) || (m<0)) return 0;
		return mul(mul(fac[n],invfac[m]),invfac[n-m]);
	}

	void math_init()
	{
		fac[0]=invfac[0]=1;
		rep(i,1,N) fac[i]=mul(fac[i-1],i);
		invfac[N]=getinv(fac[N]);
		per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
	}
	#undef N
}
using namespace My_Math;

struct node{int to,nxt;}sq[4040];
int all=0,head[2020];
int siz[2020],n,a[2020],ans[2020];
bool vis[2020];

void addedge(int u,int v)
{
	all++;sq[all].to=v;sq[all].nxt=head[u];head[u]=all;
}

void dfs(int u)
{
	int cnt=0;
	rep(i,1,n)
	{
		if (vis[i]) continue;
		if (cnt==a[u])
		{
			vis[i]=1;ans[u]=i;break;
		}
		else cnt++;
	}
	siz[u]=1;
	go(u,i)
	{
		dfs(v);siz[u]+=siz[v];
	}
	if (a[u]>siz[u]-1) {puts("NO");exit(0);}
}

int main()
{
	n=read();int rt;
	rep(i,1,n)
	{
		int fa=read();a[i]=read();
		if (!fa) rt=i;
		else addedge(fa,i);
	}
	dfs(rt);
	puts("YES");
	rep(i,1,n) printf("%d ",ans[i]);
	return 0;
}

E1&E2

先考虑E1:询问\([1,n]\)\([1,n-1]\),其中\([1,n]\)多出来的那些串就是\([i,n]\)(只不过每个串中的字符被打乱顺序),根据\([i,n]\)\([i+1,n]\)的差异就可以推出来第\(i\)个字符是什么,进而得到整个串。

再考虑E2:仿照E1我们可以得到\([1,\lfloor n/2\rfloor]\), 之后再询问一次\([1,n]\). 接下来的想法比较巧妙:记\(f_{i,x}\)为所有长度为\(i\)的子串中字符\(x\)的出现次数,那么\(f_{i+1,x}-f_{i,x}\)就是\([i+1,n-i]\)中字符\(x\)的出现次数(手画一下就是有一个前缀和一个后缀的位置没法增加,其余的位置都能多出现一次),倒推就可以得到后半部分了。

代码只放E2的,E1的被包含在里面了。

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

bool cmp(string a,string b) {return (int)a.size()>(int)b.size();}

int n,ans[110],cnt[30],f[110][26];
multiset<string> word;
string s,str[110];

void ask(int l,int r)
{
	printf("? %d %d\n",l,r);fflush(stdout);
}

void answer()
{
	printf("! ");
	rep(i,1,n) putchar(ans[i]+'a');fflush(stdout);
}

void solve1(int n)
{
	if (n==1)
	{
		ask(1,1);
		cin >> s;ans[1]=s[0]-'a';
		return;
	}
	ask(1,n);
	rep(i,1,n*(n+1)/2)
	{
		cin >> s;
		sort(s.begin(),s.end());
		word.insert(s);
	}
	multiset<string>::iterator it;
	ask(1,n-1);
	rep(i,1,n*(n-1)/2)
	{
		cin >> s;
		sort(s.begin(),s.end());
		it=word.find(s);
		word.erase(it);
	}
	it=word.begin();
	rep(i,1,n) 
	{
		str[i]=*it;it++;
	}
	sort(str+1,str+1+n,cmp);
	multiset<string>::iterator pre,now;
	pre=word.begin();now=pre;now++;
	rep(i,1,n-1)
	{
		string s1=str[i],s2=str[i+1];
		int len=s1.size();
		rep(i,0,25) cnt[i]=0;
		rep(i,0,len-1) cnt[s1[i]-'a']++;
		rep(i,0,len-2) cnt[s2[i]-'a']--;
		rep(i,0,25)
		{
			if (cnt[i])
			{
				ans[n-len+1]=i;break;
			}
		}
		pre=now;
	}
	ans[n]=str[n][0]-'a';
}

void solve2()
{
	ask(1,n);
	rep(i,1,(n+1)*n/2)
	{
		cin >> s;
		int len=s.size();
		rep(j,0,len-1) f[len][s[j]-'a']++;
	}
	per(i,(n-1)>>1,0)
	{
		int l=i+1,r=n-i;
		rep(j,0,25) cnt[j]=0;
		rep(j,l,r-1) cnt[ans[j]]++;
		rep(j,0,25)
			if (f[i+1][j]-f[i][j]>cnt[j]) {ans[r]=j;break;}
	}
}

int main()
{
	n=read();
	if (n==1)
	{
		ask(1,1);
		cin >> s;
		printf("! ");cout << s;fflush(stdout);
		return 0;
	}
	solve1(n/2);
	solve2();
	answer();
	return 0;
}

F

首先可能发生第一次碰撞的只可能是相邻两个点,否则在这个过程中一定出现了一个点“越过”另一个点从而再发生一次相撞。

于是我们可以把所有可能作为第一次碰撞的碰撞事件存下来,再按照时间排序,考虑每个事件作为答案的概率乘一乘就好了。

按时间排序后第\(i\)个碰撞发生的概率=前\(i-1\)个碰撞不发生的概率-前\(i\)个碰撞不发生的概率。求前\(i\)个不发生的概率可以用dp解决,记\(f_{i,0/1}\)表示第\(i\)个点向左/右走时所求的概率。由于一些碰撞不能发生,于是要特判一些位置无法转移。

单次dp是\(O(n)\)的,优化的话可以把dp写成矩阵的形式,之后用线段树维护即可。\(f_{i,0/1,0/1}\)表示当前区间\([l,r]\)中,\(l\)\(r\)为各个方向时的概率。再记录一下相邻位置的两个状态是否合法即可。

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=100000+100;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}

namespace My_Math{
	#define N 100000

	int fac[N+100],invfac[N+100];

	int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
	int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
	int mul(int x,int y) {return 1ll*x*y%maxd;}
	ll qpow(ll x,int y)
	{
		ll ans=1;
		while (y)
		{
			if (y&1) ans=mul(ans,x);
			x=mul(x,x);y>>=1;
		}
		return ans;
	}
	int getinv(int x) {return qpow(x,maxd-2);}

	int C(int n,int m)
	{
		if ((n<m) || (n<0) || (m<0)) return 0;
		return mul(mul(fac[n],invfac[m]),invfac[n-m]);
	}

	void math_init()
	{
		fac[0]=invfac[0]=1;
		rep(i,1,N) fac[i]=mul(fac[i-1],i);
		invfac[N]=getinv(fac[N]);
		per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
	}
	#undef N
}
using namespace My_Math;

int n,x[N],v[N],tot=0,ban[N][2][2],p[N][2];
struct enode{int dis,v,op,id;}eve[N<<2];
bool operator <(enode p,enode q) {return 1ll*p.dis*q.v<1ll*q.dis*p.v;}

struct Matrix{
	int x[2][2];
	void init() {x[0][0]=x[1][0]=x[0][1]=x[1][1]=0;}
};

namespace Segment_Tree{
	Matrix seg[N<<2];
	
	Matrix pushup(Matrix a,Matrix b,int p)
	{
		Matrix c;c.init();
		rep(i,0,1) rep(j,0,1) 
		{
			c.x[i][j]=0;
			rep(p1,0,1) rep(p2,0,1)
				if (!ban[p][p1][p2])
					c.x[i][j]=add(c.x[i][j],mul(a.x[i][p1],b.x[p2][j]));
		}
		return c;
	}
	
	void build(int id,int l,int r)
	{
		if (l==r) 
		{
			seg[id].x[0][0]=p[l][0];
			seg[id].x[1][1]=p[l][1];
			return;
		}
		int mid=(l+r)>>1;
		build(id<<1,l,mid);build(id<<1|1,mid+1,r);
		seg[id]=pushup(seg[id<<1],seg[id<<1|1],mid);
	}
	
	void modify(int id,int l,int r,int pos)
	{
		if (l==r) return;
		int mid=(l+r)>>1;
		if (pos<=mid) modify(id<<1,l,mid,pos);
		else modify(id<<1|1,mid+1,r,pos);
		seg[id]=pushup(seg[id<<1],seg[id<<1|1],mid);
	}
}
using namespace Segment_Tree;

int main()
{
	n=read();
	int inv100=getinv(100);
	rep(i,1,n)
	{
		x[i]=read();v[i]=read();
		int np=read();
		p[i][1]=mul(np,inv100);p[i][0]=dec(1,p[i][1]);
	}
	//rep(i,1,n) cout << p[i][0] << " " << p[i][1] << endl;
	rep(i,1,n-1)
	{
		eve[++tot]=(enode){x[i+1]-x[i],v[i+1]+v[i],1,i};
		if (v[i+1]>v[i]) eve[++tot]=(enode){x[i+1]-x[i],v[i+1]-v[i],2,i};
		if (v[i]>v[i+1]) eve[++tot]=(enode){x[i+1]-x[i],v[i]-v[i+1],3,i};
	}
	sort(eve+1,eve+1+tot);
	//rep(i,1,tot) printf("%d %d %d %d\n",eve[i].dis,eve[i].v,eve[i].id,eve[i].op);
	build(1,1,n);
	int ans=0,lstp=1;
	rep(i,1,tot)
	{
		int id=eve[i].id,op=eve[i].op;
		if (op==1) ban[id][1][0]=1;
		else if (op==2) ban[id][0][0]=1;
		else if (op==3) ban[id][1][1]=1;
		modify(1,1,n,id);
		int nowp=0,tim=mul(eve[i].dis,getinv(eve[i].v));
		//cout << tim << endl;
		rep(j,0,1) rep(k,0,1) nowp=add(nowp,seg[1].x[j][k]);
		int p=dec(lstp,nowp);
		ans=add(ans,mul(p,tim));
		lstp=nowp;
	}
	printf("%d",ans);
	return 0;
}

G

考虑增加一个字符\(S_i\)时答案的变化,不难发现产生贡献就是当前字符串的\(\rm{border}\).

考虑在增加了第\(i\)个字符时\(\rm{border}\)集合的变化,对\(\rm{border}\)集合中的元素\(x\),若\(S_{x+1}\neq S_i\)那么\(x\)就将从集合中移除;如果\(S_1=S_i\),那么\(1\)就会加入到\(\rm{border}\)集合中。

其实我们很容易知道了当前\(\rm{border}\)集合中的元素,就是kmp中的\(nxt[i-1],nxt[nxt[i-1]],\cdots,\)集合元素的加入很好维护,对于删除操作,对当前的\(\rm{border}\)集合维护第一个下一个字符不是\(S_i\)的位置,之后暴力向上走同时删除贡献即可,每次删除的权值可以直接用线段树维护。由于只会有\(O(n)\)个插入,所以暴力删除是没有问题的。

接下来就要考虑\(w_i\)了,也就是当前所有的权值需要对\(w_i\)\(\min\),我们可以用\(\mathrm{map}\)维护当前答案中每个权值的出现次数,之后每次暴力的找比\(w_i\)大的并修改,由于每个新增加的值至多只会被一次取\(\min\)操作涉及到,所以复杂度也是正确的。

注意最后的答案有可能会爆long long,所以需要手写一个int128.

代码写的有点丑

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<bitset>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<map>
using namespace std;
typedef long long ll;
typedef long double db;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int N=600000+100;
const db pi=acos(-1.0);
#define lowbit(x) (x)&(-x)
#define sqr(x) (x)*(x)
#define rep(i,a,b) for (register int i=a;i<=b;i++)
#define per(i,a,b) for (register int i=a;i>=b;i--)
#define go(u,i) for (register int i=head[u],v=sq[i].to;i;i=sq[i].nxt,v=sq[i].to)
#define fir first
#define sec second
#define mkp make_pair
#define pb push_back
#define maxd 998244353
#define eps 1e-8
#define mask (1<<30)-1
const ll bas=1e18;
map<int,int> mp;
int n,jmp[N],nxt[N],str[N];
char s[N];
map<int,int>::iterator it,it2;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
    while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
    return x*f;
}
 
namespace My_Math{
	#define N 100000
 
	int fac[N+100],invfac[N+100];
 
	int add(int x,int y) {return x+y>=maxd?x+y-maxd:x+y;}
	int dec(int x,int y) {return x<y?x-y+maxd:x-y;}
	int mul(int x,int y) {return 1ll*x*y%maxd;}
	ll qpow(ll x,int y)
	{
		ll ans=1;
		while (y)
		{
			if (y&1) ans=mul(ans,x);
			x=mul(x,x);y>>=1;
		}
		return ans;
	}
	int getinv(int x) {return qpow(x,maxd-2);}
 
	int C(int n,int m)
	{
		if ((n<m) || (n<0) || (m<0)) return 0;
		return mul(mul(fac[n],invfac[m]),invfac[n-m]);
	}
 
	void math_init()
	{
		fac[0]=invfac[0]=1;
		rep(i,1,N) fac[i]=mul(fac[i-1],i);
		invfac[N]=getinv(fac[N]);
		per(i,N-1,1) invfac[i]=mul(invfac[i+1],i+1);
	}
	#undef N
}
using namespace My_Math;
 
namespace Segment_Tree{
	
	int seg[N<<2];
	
	void modify(int id,int l,int r,int p,int v)
	{
		if (l==r) {seg[id]=v;return;}
		int mid=(l+r)>>1;
		if (p<=mid) modify(id<<1,l,mid,p,v);
		else modify(id<<1|1,mid+1,r,p,v);
		seg[id]=min(seg[id<<1],seg[id<<1|1]);
	}
	
	int query(int id,int l,int r,int ql,int qr)
	{
		if ((l>=ql) && (r<=qr)) return seg[id];
		int mid=(l+r)>>1,ans=2e9;
		if (ql<=mid) ans=min(ans,query(id<<1,l,mid,ql,qr));
		if (qr>mid) ans=min(ans,query(id<<1|1,mid+1,r,ql,qr));
		return ans;
	}
}
using namespace Segment_Tree;
 
pair<ull,ull> ans;
 
void addans(ull val)
{
	ans.fir+=val;
	if (ans.fir>=bas) 
	{
		ans.fir-=bas;ans.sec++;
	}
}
 
void out()
{
	if (ans.sec)
		printf("%llu%018llu\n",ans.sec,ans.fir);
	else printf("%llu\n",ans.fir);
}
 
int main()
{
	n=read();
	ull nans=0,ansc=0,answ=0;
	scanf("%s",s);int w=read();
	mp[w]++;nans+=w;ansc=w%26;answ=w;
	addans(nans);out();
	memset(str,-1,sizeof(str));
	modify(1,1,n,1,w);str[1]=s[0]-'a';
	int j=0;
	rep(i,2,n)
	{
		scanf("%s",s);w=read();
		int ch=(ansc+s[0]-'a')%26;
		w^=(answ&mask);str[i]=ch;
		modify(1,1,n,i,w);
		while ((j) && (str[j+1]!=str[i])) j=nxt[j];
		if (str[j+1]==str[i]) j++;nxt[i]=j;
		if (str[i]==str[1])
		{
			nans+=w;mp[w]++;
		}
		if (str[nxt[i-1]+1]!=str[i]) jmp[i-1]=nxt[i-1];
		else jmp[i-1]=jmp[nxt[i-1]];
		int k=nxt[i-1];
		while (k)
		{
			if (str[k+1]!=str[i])
			{
				int val=query(1,1,n,i-k,i-1);
				mp[val]--;nans-=val;
				k=nxt[k];
			}
			else k=jmp[k];
		}
		for (it=mp.upper_bound(w);it!=mp.end();)
		{
			mp[w]+=it->sec;
			nans-=1ull*(it->fir-w)*it->sec;
			it->sec=0;it2=it;it++;
			mp.erase(it2);
		}
		addans(nans);
		ansc=(ansc+nans)%26;
		answ=(answ+nans)&mask;
		out();
	}
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/encodetalker/p/12723549.html