1. 程式人生 > 其它 >2021.10.26考試總結[衝刺NOIP模擬16]

2021.10.26考試總結[衝刺NOIP模擬16]

T1樹上的數 T2時代的眼淚 T3傳統藝能 T4鋪設道路

T1 樹上的數

\(DFS\)一遍。結構體存邊好像更快?

\(code:\)

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

namespace IO{
	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<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=5000010;
int n,m,a,b,f,q,x,y,sum,ans;
int idx,head[NN];
bool vis[NN];
struct edge{ int to,nex; }e[NN];

void dfs(int s){
	if(vis[s]) return;
	--sum; vis[s]=1;
	for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to) dfs(v);
}

signed main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	sum=n=read(); m=read(); a=read(); b=read(); f=1;
	for(int i=2;i<=n;++i){
		e[++idx]=(edge){i,head[f]}; head[f]=idx;
		f=((1ll*f*a+b)^19760817)%i+1;
	}
	q=read(); x=read(); y=read();
	for(int i=1;i<=m;++i){
		dfs(q); ans^=sum;
		q=(((1ll*q*x+y)^19760817)^(i+1<<1))%(n-1)+2;
	}
	write(ans,'\n');
	return 0;
}

T2 時代的眼淚

好像挺明顯的換根\(DP\)但我因為多組詢問成功跑偏

考慮轉換貢獻,一個點的貢獻即為它字數內權值小於它的點數,所有點的貢獻之和即為答案。

首先不難求得\(1\)為根時的答案,接下來當根由\(x\)換成\(x\)的兒子\(y\)時,\(y\)的子樹變為整棵樹,\(x\)的子樹變為以\(x\)為根時樹中\(y\)子樹外的部分。

可以樹上主席樹維護資訊,但範圍\(1e6\),主席樹優秀的常數使它無法通過本題。可以用樹狀陣列在\(DFS\)中記錄遍歷完子樹後權值在某個範圍的變化量來代替。

\(code:\)

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

namespace IO{
	auto read=[]()->int{
		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<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	};
	void write(long long x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=1000010;
int n,q,u,w[NN],wn[NN],wf[NN];
long long f[NN];
int idx,head[NN];
struct edge{ int to,nex; }e[NN<<1];
int ext,has[NN];
auto add=[](int a,int b)->void{
	e[++idx]=(edge){b,head[a]}; head[a]=idx;
	e[++idx]=(edge){a,head[b]}; head[b]=idx;
};

namespace BIT{
	int c[NN];
	auto insert=[](int pos,int x)->void{ while(pos<=n+1){ c[pos]+=x; pos+=pos&-pos; } };
	auto query=[](int pos)->int{ int res=0; while(pos){ res+=c[pos]; pos-=pos&-pos; } return res; };
	void DFS(int s,int fa){
		f[1]+=query(n+1)-query(w[s]);
		insert(w[s],1);
		for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to)
			if(v!=fa) DFS(v,s);
		insert(w[s],-1);
	}
	void Dfs(int s,int fa){
		wn[s]=-query(w[s]-1);
		if(fa) wf[s]=-query(w[fa]-1);
		insert(w[s],1);
		for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to)
			if(v!=fa) Dfs(v,s);
		wn[s]+=query(w[s]-1);
		if(fa) wf[s]+=query(w[fa]-1);
	}
} using namespace BIT;

void dfs(int s,int fa){
	for(int i=head[s],v=e[i].to;i;i=e[i].nex,v=e[i].to) if(v!=fa){
		f[v]=f[s]-wn[v]+query(w[v]-1)-wf[v];
		dfs(v,s);
	}
}

signed main(){
	freopen("tears.in","r",stdin);
	freopen("tears.out","w",stdout);
	n=read(); q=read();
	for(int i=1;i<=n;i++)
		w[i]=read(),has[i]=w[i];
	sort(has+1,has+n+1); ext=unique(has+1,has+n+1)-has-1;
	for(int i=1;i<=n;i++)
		w[i]=lower_bound(has+1,has+ext+1,w[i])-has+1;
	for(int a,b,i=1;i<n;i++)
		a=read(),b=read(),add(a,b);
	DFS(1,0); Dfs(1,0); dfs(1,0);
	while(q--){
		u=read();
		printf("%lld\n",f[u]);
	}
	return 0;
}

T3 傳統藝能

對這類問題有很經典的\(DP\)。設\(f_c\)表示以\(c\)結尾的本質不同子序列個數,有

\[f_c=1+\sum_{char}f_{char} \]

這個轉移是線性的,加上本題字符集只有\(3\),可以考慮矩陣優化。可以預處理出每個字元對應的轉移矩陣,用線段樹維護矩陣即可。

\(code:\)

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

namespace IO{
	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<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010,mod=998244353;
int n,m,x,y,op,num[NN];
char t,ch[NN];
void add(int& a,int b){ a=(a+b>=mod)?(a+b-mod):(a+b); }

namespace segment_tree{
	#define ld rt<<1
	#define rd (rt<<1)|1
	struct mat{
		int s[4][4];
		mat(){}
		mat(int x){ memset(s,0,sizeof(s)); s[0][0]=s[1][1]=s[2][2]=s[3][3]=x; }
		void print(){for(int i=0;i<=3;i++)for(int j=0;j<=3;j++)write(s[i][j],j==3?'\n':' ');puts("");}
		mat operator*(const mat& a)const{
			mat res=mat(0);
			for(int i=0;i<=3;i++)
				for(int k=0;k<=3;k++)
					for(int j=0;j<=3;j++)
						add(res.s[i][j],1ll*s[i][k]*a.s[k][j]%mod);
			return res;
		}
	}st,ans,tmp,base[4],mt[NN<<2];
	void init(){
		st.s[0][0]=1;
		base[1]=mat(1); base[2]=mat(1); base[3]=mat(1);
		base[1].s[0][1]=base[1].s[1][1]=base[1].s[2][1]=base[1].s[3][1]=1;
		base[2].s[0][2]=base[2].s[1][2]=base[2].s[2][2]=base[2].s[3][2]=1;
		base[3].s[0][3]=base[3].s[1][3]=base[3].s[2][3]=base[3].s[3][3]=1;
	}
	void pushup(int rt){ mt[rt]=mt[ld]*mt[rd]; }
	void build(int rt,int l,int r){
		if(l==r) return mt[rt]=base[num[l]],void();
		int mid=l+r>>1;
		build(ld,l,mid);
		build(rd,mid+1,r);
		pushup(rt);
	}
	void update(int rt,int l,int r,int pos,int typ){
		if(l==r) return mt[rt]=base[typ],void();
		int mid=l+r>>1;
		if(pos<=mid) update(ld,l,mid,pos,typ);
		else update(rd,mid+1,r,pos,typ);
		pushup(rt);
	}
	mat query(int rt,int l,int r,int opl,int opr){
		if(l>=opl&&r<=opr) return mt[rt];
		int mid=l+r>>1;
		mat res=mat(1);
		if(opl<=mid) res=res*query(ld,l,mid,opl,opr);
		if(opr>mid) res=res*query(rd,mid+1,r,opl,opr);
		return res;
	}
} using namespace segment_tree;

signed main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n=read(); m=read();
	scanf("%s",ch+1);
	for(int i=1;i<=n;i++)
		num[i]=ch[i]-'A'+1;
	init(); build(1,1,n);
	while(m--){
		op=read();
		if(op==1){
			x=read(); cin>>t;
			update(1,1,n,x,t-'A'+1);
		} else{
			x=read(); y=read();
			ans=st*query(1,1,n,x,y);
			add(ans.s[0][1],ans.s[0][2]); add(ans.s[0][1],ans.s[0][3]);
			printf("%d\n",ans.s[0][1]);
		}
	}
	return 0;
}

T4 鋪設道路

考慮通過差分將區間問題轉化為單點問題。令\(b_i=d_{i-1}-d_i\),那麼每次操作就是選取\(l,r\),使\(b_l-1,b_r+1\),代價為\((r-l)^2\),最終要將所有\(b\)置為\(0\)。(包括\(b_{n+1}\)

要使時間最短,就要保證每次操作都是有效的,即\(b_l>0,b_r<0\)

可以維護一個佇列,掃一遍\(b\),遇到\(b>0\)則插入佇列,\(b<0\)則找隊中元素將其消去。因為\(d\geq 0\),所以一定能消完。

要求最大值則優先消隊尾元素,反之優先消隊尾元素。

\(code:\)

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

namespace IO{
	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<<1)+(x<<3)+(ch^48); ch=getchar(); }
		return x*f;
	}
	void write(int x,char sp){
		char ch[20]; int len=0;
		if(x<0) x=-x,putchar('-');
		do{ ch[len++]=x%10+'0'; x/=10; }while(x);
		for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
	}
	void ckmin(int& x,int y){ x=x<y?x:y; }
	void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010,mod=998244353;
int n,m,x,y,op,num[NN];
char t,ch[NN];
void add(int& a,int b){ a=(a+b>=mod)?(a+b-mod):(a+b); }

namespace segment_tree{
	#define ld rt<<1
	#define rd (rt<<1)|1
	struct mat{
		int s[4][4];
		mat(){}
		mat(int x){ memset(s,0,sizeof(s)); s[0][0]=s[1][1]=s[2][2]=s[3][3]=x; }
		void print(){for(int i=0;i<=3;i++)for(int j=0;j<=3;j++)write(s[i][j],j==3?'\n':' ');puts("");}
		mat operator*(const mat& a)const{
			mat res=mat(0);
			for(int i=0;i<=3;i++)
				for(int k=0;k<=3;k++)
					for(int j=0;j<=3;j++)
						add(res.s[i][j],1ll*s[i][k]*a.s[k][j]%mod);
			return res;
		}
	}st,ans,tmp,base[4],mt[NN<<2];
	void init(){
		st.s[0][0]=1;
		base[1]=mat(1); base[2]=mat(1); base[3]=mat(1);
		base[1].s[0][1]=base[1].s[1][1]=base[1].s[2][1]=base[1].s[3][1]=1;
		base[2].s[0][2]=base[2].s[1][2]=base[2].s[2][2]=base[2].s[3][2]=1;
		base[3].s[0][3]=base[3].s[1][3]=base[3].s[2][3]=base[3].s[3][3]=1;
	}
	void pushup(int rt){ mt[rt]=mt[ld]*mt[rd]; }
	void build(int rt,int l,int r){
		if(l==r) return mt[rt]=base[num[l]],void();
		int mid=l+r>>1;
		build(ld,l,mid);
		build(rd,mid+1,r);
		pushup(rt);
	}
	void update(int rt,int l,int r,int pos,int typ){
		if(l==r) return mt[rt]=base[typ],void();
		int mid=l+r>>1;
		if(pos<=mid) update(ld,l,mid,pos,typ);
		else update(rd,mid+1,r,pos,typ);
		pushup(rt);
	}
	mat query(int rt,int l,int r,int opl,int opr){
		if(l>=opl&&r<=opr) return mt[rt];
		int mid=l+r>>1;
		mat res=mat(1);
		if(opl<=mid) res=res*query(ld,l,mid,opl,opr);
		if(opr>mid) res=res*query(rd,mid+1,r,opl,opr);
		return res;
	}
} using namespace segment_tree;

signed main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	n=read(); m=read();
	scanf("%s",ch+1);
	for(int i=1;i<=n;i++)
		num[i]=ch[i]-'A'+1;
	init(); build(1,1,n);
	while(m--){
		op=read();
		if(op==1){
			x=read(); cin>>t;
			update(1,1,n,x,t-'A'+1);
		} else{
			x=read(); y=read();
			ans=st*query(1,1,n,x,y);
			add(ans.s[0][1],ans.s[0][2]); add(ans.s[0][1],ans.s[0][3]);
			printf("%d\n",ans.s[0][1]);
		}
	}
	return 0;
}