模拟赛 08.17

T1.doughnut

Aloisia有很多很多甜甜圈。有一天,她在地上画了n+1个格子,想从第1个格子跳到第n+1个格子。规则是,Aloisia每到一个格子,都会放一个甜甜圈在里面。在第i个格子时,如果当前甜甜圈的数量是偶数,那么她会跳到第i+1个格子;否则她会跳到第Pi个格子(1≤Pi≤i)。若她到达了第n+1个格子,则立即结束。Aloisia想知道,她要跳多少次才能到达终点。答案对1000000007取模。

注意到向后跳只能跳一步,且Pi<i。

并且在到达一个新的格子时,前面的格子必定是偶数。

我们可以设 f[i]f[i] 表示刚刚跳到第i个格子的步数。

那么跳到一个新的格子,肯定先会被弹飞到Pi。

可以用 f[i]f[p[i]]f[i]-f[p[i]] 表示从 p[i]p[i] 回来的步数。

最后加上1表示从i跳到i+1。

f[i+1]=f[i]+1+(f[i]f[p[i]])+1f[i+1]=f[i]+1+(f[i]-f[p[i]])+1

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int mod=1e9+7,N=1000009;
int p[N],n,ans[N];
signed main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) 
		scanf("%lld",&p[i]);
	for(int i=1;i<=n;i++)
	{
		ans[i+1]=(ans[i]+1+ans[i]-ans[p[i]]+1+mod)%mod;
		if(ans[i+1]<0) ans[i+1]+=mod;
	}
	printf("%lld",ans[n+1]);
	return 0;
}

没开longlong 100->20


T2.Pudding

Harry得到了一个字符串。
他想让Aloisia找出其中包含的(0w0)子序列有多少个。(注意0是数字,不是字母O)
为了提高难度,Harry提出他可以随时修改一个或一段字符,询问区间内的(0w0)子序列个数。
答案对4294967296取模。如果答对了的话,他就请Aloisia喝布丁奶茶。
请你帮帮Aloisia。

考虑维护(0w0)的每个子串,(,(0,0w,(0w0,(0w0),0w0),w0),0),)。

我们可以记录 f[i][j]f[i][j] 表示从i到j的(0w0)的子串。

由于是区间操作,我们考虑线段树。

区间修改可以打标记覆盖。

重点在down和update。

下放标记时,把所有子节点的f置0,再赋值为标记点->区间长度。

build的时候同理。

update时,考虑跨区间的合并贡献。

类似区间dp的方式,f[i][k]×f[k+1][j]f[i][k]\times f[k+1][j]能够贡献父亲的 f[i][j]f[i][j]

(0w0)可以直接相加合并。

在query的时候,可以利用线段树从左到右遍历的性质。

记录ans为最终答案,同时在遍历过程中用新节点更新自己。

也就是所有新来的节点都能和之前的ans作为贡献。

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define ls (x<<1)
#define rs (x<<1|1)
#define int long long
#define out puts("Debug")
const int N=50009,mod=4294967296;
char s[N],op[4];
int n,m,sum[N],ans[6][6];

struct Node
{
	int f[8][8];
	char tag;
} tr[N<<2];

inline void down(int x,int l,int r)
{
	memset(tr[ls].f,0,sizeof tr[ls].f);
	memset(tr[rs].f,0,sizeof tr[rs].f);
	int mid=(l+r)>>1;
	char c=tr[x].tag;

	tr[ls].tag=tr[rs].tag=c;

	if(c=='(') tr[ls].f[1][1]=mid-l+1;
	if(c=='0') tr[ls].f[2][2]=tr[ls].f[4][4]=mid-l+1;
	if(c=='w') tr[ls].f[3][3]=mid-l+1;
	if(c==')') tr[ls].f[5][5]=mid-l+1;

	if(c=='(') tr[rs].f[1][1]=r-mid;
	if(c=='0') tr[rs].f[2][2]=tr[rs].f[4][4]=r-mid;
	if(c=='w') tr[rs].f[3][3]=r-mid;
	if(c==')') tr[rs].f[5][5]=r-mid;

	tr[x].tag=0;
}

inline void update(int x)
{
	// memset(tr[x].f,0,sizeof tr[x].f);
	for(int i=1;i<=5;i++)
		for(int j=i;j<=5;j++)
		{
			tr[x].f[i][j]=(tr[ls].f[i][j]+tr[rs].f[i][j])%mod;
			for(int k=i;k<j;k++)
				tr[x].f[i][j]=(tr[x].f[i][j]+tr[ls].f[i][k]*tr[rs].f[k+1][j]%mod)%mod;
		}
}

inline void build(int x,int l,int r)
{
	if(l==r)
	{
		if(s[l]=='(') tr[x].f[1][1]=1;
		if(s[l]=='0') tr[x].f[2][2]=tr[x].f[4][4]=1;
		if(s[l]=='w') tr[x].f[3][3]=1;
		if(s[l]==')') tr[x].f[5][5]=1;
		tr[x].tag=0;
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	update(x);
}

inline void change(int x,int l,int r,int L,int R,char c)
{
	if(L<=l&&r<=R)
	{
		tr[x].tag=c;
		memset(tr[x].f,0,sizeof tr[x].f);

		if(c=='(') tr[x].f[1][1]=r-l+1;
		if(c=='0') tr[x].f[2][2]=tr[x].f[4][4]=r-l+1;
		if(c=='w') tr[x].f[3][3]=r-l+1;
		if(c==')') tr[x].f[5][5]=r-l+1;
		return;
	}
	if(tr[x].tag!=0)  down(x,l,r);
	int mid=(l+r)>>1;
	if(L<=mid) change(ls,l,mid,L,R,c);
	if(R>mid) change(rs,mid+1,r,L,R,c);
	update(x);
}

inline void query(int x,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)
	{
		int tmp[8][8];
		for(int i=1;i<=5;i++)
			for(int j=i;j<=5;j++)
			{
				tmp[i][j]=(ans[i][j]+tr[x].f[i][j])%mod;
				for(int k=i;k<j;k++)
					tmp[i][j]=(tmp[i][j]+ans[i][k]*tr[x].f[k+1][j]%mod)%mod;
			}
		for(int i=1;i<=5;i++)
			for(int j=i;j<=5;j++)
				ans[i][j]=tmp[i][j];//tmp辅助转移
		return;
	}
	int mid=(l+r)>>1;
	if(tr[x].tag!=0) down(x,l,r);
	if(L<=mid) query(ls,l,mid,L,R);
	if(R>mid) query(rs,mid+1,r,L,R);
	return;
}

signed main()
{
	scanf("%lld%lld",&n,&m);
	scanf("%s",s+1);
	build(1,1,n);
	while(m--)
	{
		int x,y,z;
		char ch;
		scanf("%s",op+1);
		scanf("%lld",&x);
		if(op[1]=='A')
		{
			cin>>ch;
			change(1,1,n,x,x,ch);
		}
		if(op[1]=='B')
		{
			scanf("%lld",&y);
			cin>>ch;
			change(1,1,n,x,y,ch);
		}
		if(op[1]=='C')
		{
			scanf("%lld",&y);
			memset(ans,0,sizeof ans);
			query(1,1,n,x,y);
			printf("%lld\n",ans[1][5]);
		}
	}
	return 0;
}

T3.Tiramisu

Aloisia买了一份提拉米苏。在吃之前,她决定思考一个问题来增加仪式感。她随机出一个01串和一个正整数k,然后询问自己m次:每次给出一个区间,可以进行若干次操作,选出区间内任一个长度为k的子区间取反,求最少多少次操作可以把区间全变成0。每次询问独立。

考虑对原序列异或差分,这样可以 O(1)O(1) 进行区间减。

注意到,差分的操作端点,在 modk\mod k 的意义下是相等的。

这样可以通过维护剩余系下每个数的操作次数的前缀和来计算。

无解的情况:区间中简化剩余系的次数总和为奇数,可以采用哈希 O(1)O(1) 计算。

同时注意边界,维护d,c为b数组在原序列作为端点的次数。

记住差分是 [l,r+1][l,r+1]

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll unsigned long long
const int N=2000009;
int n,k,m,a[N],b[N],sum[N],has[N],Has[N],c[N],d[N];
char s[N];
ll H=23333;

inline ll Hash()
{
	return H^=H<<13,H^=H>>17,H^=H<<5;
}

int main()
{
	// freopen("1.in","r",stdin);
	// freopen("1.out","w",stdout);
	scanf("%d%d%d",&n,&k,&m);
	scanf("%s",s+1);
	for(int i=0;i<k;i++) has[i]=Hash();
	for(int i=1;i<=n;i++) 
	{
		a[i]=s[i]-'0';
		Has[i]=Has[i-1];
		sum[i]=sum[i-1];
		int group=i%k;
		if(a[i]^a[i-1])
		{
			Has[i]^=has[group];
			sum[i]-=b[group];
			b[group]=i/k-b[group];
			sum[i]+=b[group];
		}
		c[i]=b[(i+1)%k];
		d[i]=b[i%k];
	}
	while(m--)
	{
		int l,r,ans=0;
		bool y=true;
		scanf("%d%d",&l,&r);
		if(Has[l]^Has[r]^(a[l]*has[l%k])^(a[r]*has[(r+1)%k])) {puts("-1");continue;}
		ans=sum[r]-sum[l];
		if(a[r]) ans+=(r+1)/k-2*c[r];
		if(a[l]) ans+=2*d[l]-l/k;
		printf("%d\n",ans);
	}
	return 0;
}