2022/8/1 启智树考试总结

不能贴贴题单了(悲)

所以把题面一起粘上来吧。

A.Doughnut

题目描述
Aloisia 有很多很多甜甜圈。有一天,她在地上画了 n+1 个格子,想从第 1 个格子跳到第 n+1 个格子。规则是,Aloisia 每到一个格子,都会放一个甜甜圈在里面。在第 i 个格子时,如果当前甜甜圈的数量是偶数,那么她会跳到第 i+1 个格子;否则她会跳到第 Pi 个格子(1≤Pi≤i)。
若她到达了第 n+1 个格子,则立即结束。Aloisia 想知道,她要跳多少次才能到达终点。答案对 1000000007 取模。
输入
输入的第一行包含一个正整数 n,表示一共有 n+1 个格子;第二行包含 n 个正整数 Pi,含义如题目描述中所述。
输出
输出一个整数,表示 Aloisia 需要跳的次数,对 1000000007 取模。

Solution

  • \(\mathtt{DP}\) 题。设 \(f_i\) 表示从 1 走到 \(\mathtt{i}\) 所需要的最小步数;

  • 由题目可以得到一个非常好的性质,那就是如果我走到了 \(i\),那么从 \(1~i-1\) 中的每个点肯定都经过了偶数次(因为如果经过是奇数次的话会到达 \(p_i\),而题目有 \(1\le p_i\le i\));

  • 那么我们可以写出方程:\(f_i=f_{i-1}+(f_{i-1}-f_{p_{i-1}})+2\)

  • 考场上挂了分,单纯因为没开 \(\mathtt{long\ long}\) 以及没取模……

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=1e6+10;
const int mod=1e9+7;

int p[N];
long long f[N];
int n;

int main(){
	n=read();
	for(int i=1;i<=n;++i)
		p[i]=read();
	for(int i=2;i<=n+1;++i)
		(f[i]=0ll+f[i-1]+2+(f[i-1]-f[p[i-1]]))%=mod;
	printf("%lld",(f[n+1]+mod)%mod);
	return 0;
}

B.Pudding

题目描述
Harry 得到了一个字符串。他想让 Aloisia 找出其中包含的 (0w0) 子序列有多少个。(注意 0 是数字,不是字母 O)为了提高难度,Harry 提出他可以随时修改一个或一段字符,询问区间内的 (0w0) 子序列个数。答案对 4294967296 取模。如果答对了的话,他就请 Aloisia 喝布丁奶茶。请你帮帮 Aloisia。
输入
输入的第一行包含两个整数 n、m,分别表示字符串的长度和操作的数量;
第二行包含一个长度为 n 的字符串,表示一开始的字符串;
接下来 m 行,每行一个操作,操作的格式如下:
1.A x y:表示把字符串的第 x 位改成字符 y。字符串元素从 1 开始标号。
2.B x y z:表示把字符串的第 x 位到第 y 位都改成字符 z。
3.C x y:表示询问字符串的区间 [x,y] 内有多少个子序列等于 (0w0)。
输出
输出若干行。对于每个 C 操作,输出一行,包含一个整数,表示该询问的答案,对 4294967296 取模。

Solution

如何得到60分
  • 其他都基本朴(暴)素(力)处理,把求子序列的需求用 \(\mathtt{DP}\) 处理掉;

  • 能得到答案 \(<mod\) 的60分;

60pts code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

#define ll long long
#define id(i) ((i-1)/dx+1)

const int N=5e4+10;
const ll mod=4294867296;

int n,m,dx;
char st[N];
ll f[N][6];
char tg[250];

char s(int x){
	if(tg[id(x)]) return tg[id(x)];
	return st[x];
}

void change(int l,int r,char v){
	int p=id(l),q=id(r);
	if(p==q){
		for(int i=l;i<=r;++i)
			st[i]=v;
		return ;
	}
	if(tg[p]){
		change((p-1)*dx+1,p*dx,tg[p]);
		tg[p]=0;
	}
	for(int i=l;i<=p*dx;++i)
		st[i]=v;
	for(int i=p+1;i<q;++i)
		tg[i]=v;
	if(tg[q]){
		change((q-1)*dx+1,min(n,q*dx),tg[q]);
		tg[q]=0;
	}
	for(int i=(q-1)*dx+1;i<=r;++i)
		st[i]=v;
	return ;
}

void ask(int l,int r){
	memset(f,0,sizeof(f));
	for(int i=l;i<=r;++i){
		(f[i-l+1][0]=f[i-l][0]+(s(i)=='('))%=mod;
		(f[i-l+1][1]=f[i-l][1]+1ll*(s(i)=='0')*f[i-l][0])%=mod;
		(f[i-l+1][2]=f[i-l][2]+1ll*(s(i)=='w')*f[i-l][1])%=mod;
		(f[i-l+1][3]=f[i-l][3]+1ll*(s(i)=='0')*f[i-l][2])%=mod;
		(f[i-l+1][4]=f[i-l][4]+1ll*(s(i)==')')*f[i-l][3])%=mod;
	}
	printf("%lld\n",f[r-l+1][4]);
	return ;
}

int main(){
	n=read(),m=read();
	scanf("%s",st+1);
	dx=sqrt(n);
	char opt,v;
	int x,y;
	while(m--){
		cin>>opt,x=read();
		if(opt=='A'){
			cin>>v;
			st[x]=v;
		}
		else if(opt=='B'){
			y=read(),cin>>v;
			change(x,y,v);
		}
		else{
			y=read();
			ask(x,y);
		}
	}
	return 0;
}//属于是暴力? 
正解
  • 线段树。用线段树处理单点修改和区间修改,并给每个节赋一个 \(5\times 5\) 大小的数组,用于统计 \(\mathtt{(0w0)}\) 的每一个子段在线段树的这一段里出现的次数;

  • 状态转移时和 \(\mathtt{DP}\) 很相似;

  • 第一发没过居然是因为 \(mod\) 打错了……

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

#define ll long long

const int N=5e4+10;
const ll mod=4294967296;

int n,m;
char st[N];

struct memr{
	int l,r;
	char v,tg;
	ll ans[5][5];
}tr[N<<3];

void add(int p,char v){
	if(v=='(')
		tr[p].ans[0][0]+=tr[p].r-tr[p].l+1;
	else if(v=='0'){
		tr[p].ans[1][1]+=tr[p].r-tr[p].l+1;
		tr[p].ans[3][3]+=tr[p].r-tr[p].l+1;
	}
	else if(v=='w')
		tr[p].ans[2][2]+=tr[p].r-tr[p].l+1;
	else if(v==')')
		tr[p].ans[4][4]+=tr[p].r-tr[p].l+1;
	return ;
}

void mul(memr x,memr y,memr &a){
	memset(a.ans,0,sizeof(a.ans));
	for(int s=0;s<5;++s)
		for(int e=s;e<5;++e){
			for(int i=s;i<e;++i)
				(a.ans[s][e]+=1ll*x.ans[s][i]*y.ans[i+1][e])%=mod;
			(a.ans[s][e]+=0ll+x.ans[s][e]+y.ans[s][e])%=mod;
		}
	return ;
}

void pushup(int p){
	memset(tr[p].ans,0,sizeof(tr[p].ans));
	mul(tr[p<<1],tr[p<<1|1],tr[p]);
	return ;
}

void pushdown(int p){
	if(tr[p].tg){
		tr[p<<1].tg=tr[p].tg;
		tr[p<<1].v=tr[p].tg;
		tr[p<<1|1].tg=tr[p].tg;
		tr[p<<1|1].v=tr[p].tg;
		memset(tr[p<<1].ans,0,sizeof(tr[p<<1].ans));
		memset(tr[p<<1|1].ans,0,sizeof(tr[p<<1|1].ans));
		add(p<<1,tr[p].tg),add(p<<1|1,tr[p].tg);
		tr[p].tg=0;
	}
	return ;
}

void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r){
		tr[p].v=st[l];
		memset(tr[p].ans,0,sizeof(tr[p].ans));
		add(p,st[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
	return ;
}

void change(int p,int l,int r,char v){
	if(l<=tr[p].l && tr[p].r<=r){
		tr[p].tg=v;
		tr[p].v=v;
		memset(tr[p].ans,0,sizeof(tr[p].ans));
		add(p,v);
		return ;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)>>1;
	if(l<=mid) change(p<<1,l,r,v);
	if(mid<r) change(p<<1|1,l,r,v);
	pushup(p);
	return ;
}

void ask(int p,int l,int r,memr &d){
	if(l<=tr[p].l && tr[p].r<=r){
		memr cnt=d;
		mul(cnt,tr[p],d);
		return ;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)>>1;
	if(l<=mid) ask(p<<1,l,r,d);
	if(mid<r) ask(p<<1|1,l,r,d);
	return ;
}

int main(){
	n=read(),m=read();
	scanf("%s",st+1);
	build(1,1,n);
	char opt,v;
	int x,y;
	while(m--){
		cin>>opt,x=read();
		if(opt=='A'){
			cin>>v;
			change(1,x,x,v);
		}
		else if(opt=='B'){
			y=read(),cin>>v;
			change(1,x,y,v);
		}
		else{
			y=read();
			memr cnt;
			memset(cnt.ans,0,sizeof(cnt.ans));
			ask(1,x,y,cnt);
			printf("%lld\n",cnt.ans[0][4]);
		}
	}
	return 0;
}
posted @ 2022-08-01 16:18  Star_LIcsAy  阅读(104)  评论(0)    收藏  举报