CSP模拟<反思> (21~31)

csp模拟21#

ARC141F#

首先上结论:如果一个串能用其他串消完那么这个串可以删去;

剩下的串中有 SiSj 的子串,那么答案是 Yes;

如果存在 Si=A+BSj=B+C,且 AC 则答案是 Yes.

第一部分:如何判断一个串 Si 是否能被消完。

建 AC 自动机,预处理 mark 表示其点 fail 树祖先上的某一个终止节点。开一个栈,栈里存在树上的编号,每压入一个点,从这个点跳 mark 匹配到终止节点,在栈中删去串的长度。匹配到最后如果栈中没元素,则此串可以删去,如果有元素且小于原始长度,则答案 Yes。

第二部分,对筛选出来的串在 AC自动机里跑一边,记录每个点被经历的次数和被谁经历。如果经历次数大于 1,则答案为 Yes。

fi,t(tleni)表示 Si 长为 t 的后缀,可以匹配到的某一个 Sj 的前缀。可以对每个串跳 fail 求出。

接着对于每个 j=fi,t,只要存在 lenilenjfj,lenit==i 那么这就是坏串,则答案为 Yes

不为 Yes 则为 No。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e6+10;
string s[N];
int tr[N][4],tot=0,fail[N],en[N],len[N],rk[N],d[N],idx[N],pk[N];
map<int,int>f[N];
int b[N];
bool vis[N];
int st[N],top;
int head[N],nex[N],ver[N],tot_node=0,mark[N][2];
queue<int> q;
void add(int x,int y){
	ver[++tot_node]=y;nex[tot_node]=head[x],head[x]=tot_node;
}
void insert(string s1,int id){
	int u=0;
	for(int i=0;s1[i];i++){
		int ch=s1[i]-'A';
		if(!tr[u][ch]) tr[u][ch]=++tot;
		d[tr[u][ch]]=d[u]+1;
		u=tr[u][ch];
	}
	en[u]++;
	rk[id]=u;
}
void build(){
	for(int i=0;i<4;i++){
		if(tr[0][i]){
			add(0,tr[0][i]);
			q.push(tr[0][i]); 
		}
	}
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=0;i<4;i++){
			if(tr[u][i]){
				fail[tr[u][i]]=tr[fail[u]][i];
				add(fail[tr[u][i]],tr[u][i]);
				q.push(tr[u][i]); 
			}
			else{
				tr[u][i]=tr[fail[u]][i];
			}
		}
	}
}
void dfs(int x,int f){
	mark[x][0]=mark[f][0];
	if(en[x]){
		mark[x][0]=x;
		mark[x][1]=mark[f][0];	
	}
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==f) continue;
		dfs(y,x);
	}
}

void ask(string s1,int id){
	int u=0;
	for(int i=0;s1[i];i++){
		int ch=s1[i]-'A';
		u=tr[u][ch];
		pk[u]=id;
		idx[u]++;
	}
} 
void clear(int x){
	tot=0,tot_node=0;
	memset(tr,0,sizeof(tr));
	memset(fail,0,sizeof(fail));
	memset(mark,0,sizeof(mark));
	memset(en,0,sizeof(en));
	memset(len,0,sizeof(len));
	memset(rk,0,sizeof(rk));
	memset(d,0,sizeof(d));
	memset(idx,0,sizeof(idx));
	memset(pk,0,sizeof(pk));
	memset(head,0,sizeof(head));
	memset(nex,0,sizeof(nex));
	memset(ver,0,sizeof(ver));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=x;i++){
		f[i].clear();
	}
}

int main(){
	int T=1;
	while(T--){
		int n;
		scanf("%d",&n);
		clear(n);
		for(int i=1;i<=n;i++){
			cin>>s[i];
			len[i]=s[i].size();
			insert(s[i],i);
		}
		build();
		dfs(0,0);
		int getans=0;
		for(int k=1;k<=n;k++){
			int u=0;
			top=0;
			for(int i=0;i<len[k];i++){
				u=tr[st[top]][s[k][i]-'A'];
				st[++top]=u;
				if(rk[k]!=u && mark[u][0]) top-=d[mark[u][0]];
				if(rk[k]==u && mark[u][1]) top-=d[mark[u][1]];
			}
			if(top==0) vis[k]=1;
			if(top && top<len[k]){
				printf("Yes\n");
				getans=1;
				break;
			}
		}
		if(getans) continue;
		for(int i=1;i<=n;i++){
			if(vis[i]) continue;
			ask(s[i],i);
		}
		for(int i=1;i<=n;i++){
			if(vis[i]) continue;
			int tmp=rk[i];
			int last=0;
			while(fail[tmp]){
				tmp=fail[tmp];
				if(last==d[tmp]){
					printf("Yes\n");
					getans=1;
					break;
				}
				f[i][d[tmp]]=pk[tmp];
				last=d[tmp];
			}
			if(getans) break;
		}
		if(getans) continue;
		for(int i=1;i<=n;i++){
			if(vis[i]) continue;
			int tmp=rk[i];
			while(fail[tmp]){
				tmp=fail[tmp];
				int t=d[tmp];
				int j=f[i][t];
				if(!j) continue;
				if(len[i]!=len[j] ||f[j][len[i]-t]!=i){
					printf("Yes\n");
					getans=1;
					break;
				}
			}
			if(getans) break;
		}
		if(getans) continue;
		printf("No\n");
	}
}
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=8*1e5+10;
int a[N],head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int d[N],top[N],fa[N],son[N],size[N],ed[N],id[N],cnt=0,rk[N];
struct asd{
	int lazy;
	int l,r;
	int p,op;
	int lmax,lmin;
	int nmax,nmin;
}tr[N*4];
int lson[N],rson[N];
void dfs1(int x,int f){
	fa[x]=f;
	d[x]=d[f]+1;
	size[x]=1;
	if(lson[x]){
		dfs1(lson[x],x);
		size[x]+=size[lson[x]];
		if(size[son[x]]<size[lson[x]]){
			son[x]=lson[x];
		}
	}
	if(rson[x]){
		dfs1(rson[x],x);
		size[x]+=size[rson[x]];
		if(size[son[x]]<size[rson[x]]){
			son[x]=rson[x];
		}
	}
}
void dfs2(int x,int tp){
	id[x]=++cnt;
	rk[cnt]=x;
	top[x]=tp;
	if(son[x]) dfs2(son[x],tp);
	if(lson[x] && lson[x]!=son[x]){
		dfs2(lson[x],lson[x]);
	}
	if(rson[x] && rson[x]!=son[x]){
		dfs2(rson[x],rson[x]);
	}
	ed[x]=cnt;
}
void pushup(int p){
	tr[p].lmax=max(tr[p*2].lmax,tr[p*2+1].lmax);
	tr[p].lmin=min(tr[p*2].lmin,tr[p*2+1].lmin);
	tr[p].nmax=max(tr[p*2].nmax,tr[p*2+1].nmax);
	tr[p].nmin=min(tr[p*2].nmin,tr[p*2+1].nmin);
}
void pushdown(int p){
	if(tr[p].lazy){
		tr[p].lazy^=1;
		tr[p*2].lazy^=1,tr[p*2+1].lazy^=1;
		swap(tr[p*2].lmax,tr[p*2].nmax);
		swap(tr[p*2].lmin,tr[p*2].nmin);
		swap(tr[p*2+1].lmax,tr[p*2+1].nmax);
		swap(tr[p*2+1].lmin,tr[p*2+1].nmin);
	}
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	tr[p].lazy=0;
	if(l==r){
		if(lson[fa[rk[l]]]==rk[l]){
			tr[p].nmin=a[fa[rk[l]]];tr[p].nmax=INT_MIN;
			tr[p].lmax=a[fa[rk[l]]];tr[p].lmin=INT_MAX;
		}
		else{
			tr[p].nmax=a[fa[rk[l]]];tr[p].nmin=INT_MAX;
			tr[p].lmin=a[fa[rk[l]]];tr[p].lmax=INT_MIN;
		}
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	pushup(p);
}

int query(int p,int l,int r,int op){
	if(tr[p].l>=l && tr[p].r<=r){
		if(op==1) return tr[p].nmin;////////1 : small
		else return tr[p].nmax;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(op==1){
		int ans=INT_MAX;
		if(l<=mid) ans=min(ans,query(p*2,l,r,op));
		if(r>mid) ans=min(ans,query(p*2+1,l,r,op));
		return ans;
	}
	else{
		int ans=INT_MIN;
		if(l<=mid) ans=max(ans,query(p*2,l,r,op));
		if(r>mid) ans=max(ans,query(p*2+1,l,r,op));
		return ans;
	}
}
void change(int p,int wh,int v){
	if(tr[p].l==tr[p].r){
		if(tr[p].lmin==a[fa[rk[wh]]]) tr[p].lmin=v;
		if(tr[p].lmax==a[fa[rk[wh]]]) tr[p].lmax=v;
		if(tr[p].nmin==a[fa[rk[wh]]]) tr[p].nmin=v;
		if(tr[p].nmax==a[fa[rk[wh]]]) tr[p].nmax=v;
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh,v);
	else change(p*2+1,wh,v);
	pushup(p);
}
int ask_mi(int x){
	int fx=top[x];
	int ans=INT_MAX;
	while(fx>1){
		ans=min(ans,query(1,id[fx],id[x],1));
		x=fa[fx],fx=top[x];
	}
	ans=min(ans,query(1,id[1]+1,id[x],1));
	return ans;
}

int ask_ma(int x){
	int fx=top[x];
	int ans=INT_MIN;
	while(fx>1){
		ans=max(ans,query(1,id[fx],id[x],0));
		x=fa[fx],fx=top[x];
	}
	ans=max(ans,query(1,id[1]+1,id[x],0));
	return ans;
}

void chan(int p,int l,int r){
	if(tr[p].l>=l && tr[p].r<=r){
		tr[p].lazy^=1;
		swap(tr[p].lmax,tr[p].nmax);
		swap(tr[p].lmin,tr[p].nmin);
		return;
	}
	pushdown(p);
	int mid=(tr[p].l+tr[p].r)/2;
	if(l<=mid) chan(p*2,l,r);
	if(r>mid) chan(p*2+1,l,r);
	pushup(p);
}
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		int x,y;
		scanf("%d%d",&x,&y);
		if(x)lson[i]=x;
		if(y)rson[i]=y;
	}
	dfs1(1,0);
	dfs2(1,0);
	build(1,1,cnt);
	for(int i=1;i<=m;i++){
		int opt,x,y;
		scanf("%d%d",&opt,&x);
		if(opt==3){
			int mi=ask_mi(x);
			int ma=ask_ma(x);
			if(a[x]>=mi || a[x]<=ma) printf("NO\n");
			else printf("YES\n");
		}
		if(opt==1){
			scanf("%d",&y);
			if(lson[x])	change(1,id[lson[x]],y);
			if(rson[x]) change(1,id[rson[x]],y);
			a[x]=y;
		}
		if(opt==2){
			chan(1,id[x]+1,ed[x]);
		}
		
	}
}

csp模拟24#

监狱#

首先得到一个结论,如果存在方案,一定有一种方案使每个人都是从起点不停歇地走到终点。因为如果一个合法方案中,A 因为等之后路径上的 B 离开在中间停下了,完全可以进行调整使得 B 先到达终点,A 再从起点出发。那么我们将问题简化为是否存在一个排列,人按这个顺序出发直接走到终点,中途不会遇到其他人。

分析一下题目,发现限制只有两条:

  • 如果 A 的起点在 B 的路径上,那么A 必须先于 B 走。
  • 如果 A 的终点在 B 的路径上,那么B 必须先于 A 走。

那么将 A 必须先于 B 走表示为 AB 连边,那么就看这个图有没有拓扑序,拓扑排序判一下即可

我们首先将原图的树复制两棵 S,T,树 S 儿子连向父亲,树 T 父亲连向儿子(线段树优化建图)。用 Sx,Tx 表示原图中点 x 在这两棵树上的编号,还有连接两树的节点 pi(1<=i<=m) 表示罪犯。

对于第 i 个罪犯的路径:

  • 对于路径上的终点,i 必须先于终点在这条路径上的人走,所以 pi 节点连向这些点在 T 中的区间。
  • 对于路径上的起点,这些点必须先于 i 先走,所以 这些点在 S 中的区间连向 pi
  • 为了传递限制,还需对每个 piSsi 连边(si 为叶子节点编号,pi 为该节点上的起点)。
  • 为了传递限制,还需对每个 Ttipi 连边(ti 为叶子节点编号,pi 为该节点上的终点)。

可以对原树树剖,边跳 top 边建边,可以用线段树维护,联想到线段树优化建图,时空复杂度 O(nlog2n)

当然这里也可以用倍增优化建图做路径连边,复杂度 O(nlogn),更为优秀。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=120005;
const int M=N*50;
int s[N],t[N];
int pi[N],du[M];
int head[N*2],ver[N*2],nex[N*2],tot=0;
void add(int x,int y){
	ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int head_t[M],ver_t[M],nex_t[M],tot_t=0;
void add_t(int x,int y){
	ver_t[++tot_t]=y,nex_t[tot_t]=head_t[x],head_t[x]=tot_t;
	du[y]++;
}
int fa[N],top[N],siz[N],d[N],son[N],id[N],tot_d=0;
int cnt_t,rt1,rt2;
void dfs1(int x,int f){
	fa[x]=f;
	siz[x]=1;
	d[x]=d[f]+1;
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==f) continue;
		dfs1(y,x);
		siz[x]+=siz[y];
		if(siz[son[x]]<siz[y]){
			son[x]=y;
		}
	}
}

void dfs2(int x,int tp){
	top[x]=tp;
	id[x]=++tot_d;
//	rk[tot_d]=x;
	if(son[x]) dfs2(son[x],tp);
	for(int i=head[x];i;i=nex[i]){
		int y=ver[i];
		if(y==fa[x]) continue;
		if(y!=son[x]) dfs2(y,y);
	}
}

struct Segment_tree{
	int l,r;
}tr[M];

int S[N],T[N];

void buildt(int &p,int l,int r){
	if(!p) p=++cnt_t;
	if(l==r){
		T[l]=p;
		return;
	}
	int mid=(l+r)/2;
	buildt(tr[p].l,l,mid);
	buildt(tr[p].r,mid+1,r);
	add_t(p,tr[p].l);
	add_t(p,tr[p].r);
}

void builds(int &p,int l,int r){
	if(!p) p=++cnt_t;
	if(l==r){
		S[l]=p;
		return;
	}
	int mid=(l+r)/2;
	builds(tr[p].l,l,mid);
	builds(tr[p].r,mid+1,r);
	add_t(tr[p].l,p);
	add_t(tr[p].r,p);
}

void changet(int p,int wh,int l,int r,int id){
	if(l==r){
		add_t(p,pi[id]);
		return;
	}
	int mid=(l+r)/2;
	if(wh<=mid) changet(tr[p].l,wh,l,mid,id);
	else changet(tr[p].r,wh,mid+1,r,id);
}

void changes(int p,int wh,int l,int r,int id){
	if(l==r){
		add_t(pi[id],p);
		return;
	}
	int mid=(l+r)/2;
	if(wh<=mid) changes(tr[p].l,wh,l,mid,id);
	else changes(tr[p].r,wh,mid+1,r,id);
}

void changet1(int p,int l,int r,int L,int R,int id){
	if(l>r) return;
	if(L>=l && R<=r){
		add_t(pi[id],p);
		return;
	}
	int mid=(L+R)/2;
	if(l<=mid) changet1(tr[p].l,l,r,L,mid,id);
	if(r>mid) changet1(tr[p].r,l,r,mid+1,R,id);
}

void changes1(int p,int l,int r,int L,int R,int id){
	if(l>r) return;
	if(L>=l && R<=r){
		add_t(p,pi[id]);
		return;
	}
	int mid=(L+R)/2;
	if(l<=mid) changes1(tr[p].l,l,r,L,mid,id);
	if(r>mid) changes1(tr[p].r,l,r,mid+1,R,id);
}

void workt(int x,int y,int op){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(d[fx]>d[fy]){
			int as=id[fx],bs=id[x];
			changet1(rt1,as,bs,1,tot_d,op);
			if(x==s[op]) changes1(rt2,as,bs-1,1,tot_d,op);
			else changes1(rt2,as,bs,1,tot_d,op);
			x=fa[fx],fx=top[x];
		}
		else{
			int as=id[fy],bs=id[y];
			if(y==t[op]) changet1(rt1,as,bs-1,1,tot_d,op);
			else changet1(rt1,as,bs,1,tot_d,op);
			changes1(rt2,as,bs,1,tot_d,op);
			y=fa[fy],fy=top[y];
		}
	}
	int as=id[x],bs=id[y];
	if(id[x]<id[y]){
		if(y==t[op]) bs--;
		changet1(rt1,id[x],bs,1,tot_d,op);
		if(x==s[op]) as++;
		changes1(rt2,as,id[y],1,tot_d,op);
	}
	else{
		if(y==t[op]) bs++;
		changet1(rt1,bs,id[x],1,tot_d,op);
		if(x==s[op]) as--;
		changes1(rt2,id[y],as,1,tot_d,op);
	}
}
queue<int> q;
void clear(int n,int m){
	for(int i=1;i<=cnt_t;i++) head_t[i]=du[i]=0;
	for(int i=1;i<=n;i++) head[i]=siz[i]=son[i]=fa[i]=top[i]=siz[i]=d[i]=id[i]=0;
	for(int i=1;i<=m;i++) pi[i]=s[i]=t[i]=0;
	for(int i=1;i<=cnt_t;i++){
		tr[i].l=tr[i].r=0;
	}
	tot=tot_t=tot_d=cnt_t=0;
	rt1=rt2=0;
}
signed main(){
//	freopen("prison.in","r",stdin);
//	freopen("prison.out","w",stdout);
	int pp;
	scanf("%d",&pp);
	while(pp--){
		int n;
		scanf("%d",&n);
		for(int i=1;i<n;i++){
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y),add(y,x);
		}
		dfs1(1,0);
		dfs2(1,0);
		cnt_t=0;
		buildt(rt1,1,tot_d);
		builds(rt2,1,tot_d);
		int m;
		scanf("%d",&m);
		for(int i=1;i<=m;i++){
			pi[i]=++cnt_t;
		}
		for(int i=1;i<=m;i++){
			scanf("%d%d",&s[i],&t[i]);
			changet(rt1,id[t[i]],1,tot_d,i);
			changes(rt2,id[s[i]],1,tot_d,i);
		}
		for(int i=1;i<=m;i++){
			workt(s[i],t[i],i);
		}
		for(int i=1;i<=cnt_t;i++){
			if(du[i]==0){
				q.push(i); 
			}
		}
		while(!q.empty()){
			int x=q.front();
			q.pop();
			for(int i=head_t[x];i;i=nex_t[i]){
				int y=ver_t[i];
				du[y]--;
				if(du[y]==0) q.push(y);
			}
		}
		int ans=0;
		for(int i=1;i<=m;i++){
			if(du[pi[i]]) {
				ans=1;
				break;
			}
		}
		if(ans==1) printf("No\n");
		else printf("Yes\n");
		clear(n,m);
	}
}

[AGC023E] Inversions#

引流

再补充一点点:
对于 aiaj 我们要倒着扫一遍,扫到 i 时,我们要查询在此后的大于 a[i]prod 之和然后除以 a[i],最后再乘上一个 tot2.

对于 aiaj 我们要正着扫一遍,扫到 i 时,我们要查询在此前的大于 a[i]prod 之和然后除以 a[i],最后再乘上一个 tot2。但是因为 i<jaiaj,求得是正序对的个数,所以要用总的 totgs 减去正序对就是逆序对。gs 表示大于 ai 的个数,(与前缀区分,不用考虑 prod 为0),均利用树状数组维护。

取模取尽

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4*1e5+5;
const int mod=1e9+7;
int a[N],cnt[N],sum[N];
int prod[N];
int n;
int c[N],d[N];
int mgml(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	for(;x<=N;x+=lowbit(x)) c[x]=(c[x]+v)%mod;
}
int ask(int x){
	if(x<=0) return 0;
	int ans=0;
	for(;x;x-=lowbit(x)) ans=(ans+c[x])%mod;
	return ans;
}
void add_1(int x,int v){
	for(;x<=N;x+=lowbit(x)) d[x]=(d[x]+v)%mod;
}
int ask_1(int x){
	if(x<=0) return 0;
	int ans=0;
	for(;x;x-=lowbit(x)) ans=(ans+d[x])%mod;
	return ans;
}
int nex0[N];
signed main(){
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum[a[i]]++;
    }
    int tot=1;
    for(int i=n;i>=1;i--){
    	sum[i]=(sum[i]+sum[i+1])%mod;
    	cnt[i]=sum[i]-(n-i);
    	tot=tot*cnt[i]%mod;
	}
	prod[0]=1;
	for(int i=1;i<=n;i++){
		int sum=(cnt[i]-1)*mgml(cnt[i],mod-2)%mod;
		prod[i]=prod[i-1]*sum%mod;
		if(cnt[i]==1){
			while(cnt[i]==1){
				i++;
			}
			prod[i]=(cnt[i]-1)*mgml(cnt[i],mod-2)%mod;
		}
	}
	int wh=n+1;
	for(int i=n;i>=1;i--){
		if(prod[i]==0) wh=i;
		nex0[i]=wh;
	}
	int ans=0;
	for(int i=n;i>=1;i--){
		int sum=(ask(nex0[a[i]])-ask(a[i]-1)+mod)%mod;
		ans=(ans+sum*mgml(prod[a[i]],mod-2)%mod)%mod;
		add(a[i],prod[a[i]]);
	}
	ans=(ans*tot%mod*mgml(2,mod-2)%mod+mod)%mod;
	int anss=0;
	memset(c,0,sizeof(c));
	for(int i=1;i<=n;i++){
		int sum=(ask(nex0[a[i]])-ask(a[i])+mod)%mod;
		int to=(ask_1(n)-ask_1(a[i])+mod)%mod;
		int cnt=(tot*to%mod-tot*sum%mod*mgml(prod[a[i]]*2%mod,mod-2)%mod+mod)%mod;
		anss=(anss+cnt)%mod;
		add(a[i],prod[a[i]]);
		add_1(a[i],1);
	}
	cout<<(ans+anss+mod)%mod;
    
}

csp模拟29#

坍缩#

学到了对于字符串回文的处理。

对每个字符定义 2c 把他二进制存储,看这个字符串的异或值得 popcount 得知,若为1,则存在一个字符出现奇数次,若大于1 ,则不可以组成回文串。

竖着得字符串对每个字符出现次数进行 manacher ,将每个字符串存成 131c

点击查看代码
#include<bits/stdc++.h>
#define int long long

using namespace std;
const int mod=993244853;
char s[300][300];
int has[300];
int f[300],g[300];
int _xorhas[300];
bool flat[300];

int min(int a,int b){
	return a<b?a:b;
} 

int manacher(int n){
	int ans=0;
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g)); 
	for(int i=1,l=0,r=-1;i<=n;++i){
		if(flat[i]) continue;
		int k;
		if(i>r) k=1;
		else k=min(r-i+1,f[r+l-i]);
		while( i-k>=1 && i+k<=n &&has[i-k]==has[i+k] && flat[i-k]==0 && flat[i+k]==0) k++;
		f[i]=k--;
		ans+=f[i];
		if(r<i+k){
			r=i+k;
			l=i-k;
		}
	}
	for(int i=1,l=0,r=-1;i<=n;++i){
		if(flat[i] || flat[i-1]) continue;
		int k;
		if(i>r) k=0;
		else k=min(r-i+1,g[l+r-i+1]);
		while(i-k-1>=1 && i+k<=n && has[i-k-1]==has[i+k] && flat[i-k-1]==0 && flat[i+k]==0) k++;
		g[i]=k--;
		ans+=g[i];
		if(r<i+k){
			r=i+k;
			l=i-k-1;
		}
	}
	return ans; 
}
int p(char x){
	return (1<<(x-'a'));
}
int sum[300][300];
int sum1[300][300]; 
int base[30];
int po[30];
signed main(){
	int n,m;
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%s",s[i]+1);
	}
	po[0]=1;
	for(int i=1;i<=26;i++){
		po[i]=po[i-1]*113%mod;
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			sum[i][j]=sum[i][j-1]^(1ll<<(s[i][j]-'a'));
			sum1[i][j]=(sum1[i][j-1]+po[s[i][j]-'a'])%mod;
		}
	}
	int ans=0;
	for(int i=1;i<=m;++i){
		for(int j=i;j<=m;++j){
			for(int k=1;k<=n;++k){
				flat[k]=0;
				int hass=(sum1[k][j]-sum1[k][i-1]+mod)%mod;
				int xo=sum[k][j]^sum[k][i-1]%mod;
				has[k]=hass;
				if(__builtin_popcountll(xo)>1) flat[k]=1; 
			}
			ans+=manacher(n);
		}
	}
	printf("%lld",ans); 
} 

csp模拟30#

AT_agc019_f [AGC019F] Yes or No#

假设 m>n
这张图是对于当前局势我们进行的最优决策,然而答案并不是这个样子,而你答对的题数是原本答案折线与红线重合部分,思考发现一定有 m 条线段与红线重合,唯一不确定的就是就是 x=y 的斜线上的选择是否正确。我们均假设 x=y 向左走,则计算答案与之重合的期望贡献,对与一个直线上的点 (i,i) ,经过这个点的答案折线方案有 C(2i,i)C(n+m2i,ni) 概率就是再除以 C(n+m,n) 期望贡献就是再乘上 12 ,然后对每个点求和。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=1e6+5;
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
int fac[N],ifac[N];
int n,m;
void init(){
	fac[0]=ifac[0]=1;
	for(int i=1;i<=n+m;i++){
		fac[i]=fac[i-1]*i%mod;
		ifac[i]=mgml(fac[i],mod-2);
	}
}
int C(int x,int y){
	return fac[x]*ifac[x-y]%mod*ifac[y]%mod;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	if(n>m) swap(n,m);
	int sum=0;
	init();
	for(int i=1;i<=n;i++){
		sum+=C(2*i,i)*C(n+m-2*i,n-i)%mod;
		sum%=mod;
	}
	cout<<(sum*mgml(2,mod-2)%mod*mgml(C(n+m,n)%mod,mod-2)%mod+m)%mod;
	
}

#

还是对这种约瑟夫似的问题偏弱。

对于一个长度为 i 的序列,进行一次挽回,会删掉 1+i1k+1 个叶子,所以上一个状态 last=i(1+i1k+1) ,因为进行了反转,所以原本的 ans[last] 变成了 pos=lastans[last]+1 ,再考虑起点与这个点之间删除的个数就是 1+pos1k ,因为已经删去了一个点。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=7*1e6+5;
int a[N];
bool flat[N];
int q,m;
int ans[N];
signed main(){
	int T;
	scanf("%lld",&T);
	for(int k=1;k<=T;++k){
		scanf("%lld%lld",&q,&m);
		ans[1]=1;
		for(int i=2;i<=m;i++){
			int last=i-1-(i-1)/(k+1);
			int p=last-ans[last]+1;
			ans[i]=p+1+(p-1)/k;
		}
		
		for(int i=1;i<=q;i++){
			int x;
			scanf("%lld",&x);
			printf("%lld ",ans[x]);
		}
		printf("\n");
	}
}
/*
1
7 10 6
*/

楼房重建#

很神奇的线段树 pushup .

线段树上维护区间最大值 ma ,答案贡献 ans.

合并左右儿子时,做儿子的答案是一定要的,设左儿子最大值为 ma ,考虑右儿子,将右儿子分为左右两了儿子 lsonrson ,如果 lson 最大值大于 ma ..........................fk,晚上咋还加了比赛。。。。。。。。。。。。。。。。。。。。。。。。。。。,则右儿子贡献可以加上,递归左儿子;相反,左儿子无贡献,递归右儿子。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct asd{
	int l,r,sum;
	double ma;
}tr[N*4];
void build(int p,int l,int r){
	tr[p].l=l;tr[p].r=r;
	tr[p].ma=0;
	if(l==r) return;
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}
int pushup(double ma,int p){
	if(tr[p].l==tr[p].r) return tr[p].ma>ma;
	if(tr[p*2].ma<=ma) return pushup(ma,p*2+1);
	else return pushup(ma,p*2)+tr[p].sum-tr[p*2].sum;
}
void change(int p,int wh,double v){
	if(tr[p].l==tr[p].r){
		tr[p].sum=1,tr[p].ma=v;
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh,v);
	else change(p*2+1,wh,v);
	tr[p].ma=max(tr[p*2].ma,tr[p*2+1].ma);
	tr[p].sum=tr[p*2].sum+pushup(tr[p*2].ma,p*2+1);
}

int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	build(1,1,n);
	for(int i=1;i<=m;i++){
		double x,y;
		scanf("%lf%lf",&x,&y);
		change(1,x,y/x);
		int ans=tr[1].sum;
		printf("%d\n",ans);
	} 
}

csp模拟31#

你相信引力吗#

很巧妙的一道单调栈问题。

首先它是一个递减栈,加入一个元素,如果它比栈顶元素大,那么弹栈并记录贡献,因为是显然的。最后将其

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17632365.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu