【考试总结】2022-05-30

卡片

对每张牌计算最后它是正面朝上还是反面朝上

找到 [min(Ai,Bi),max(Ai,Bi)) 区间中最后一次出现元素的出现位置,那么在这里这张牌必然是 max(Ai,Bi) 朝上

剩下的工作就是后缀查有几个元素 max(Ai,Bi)

Code Display
const int N=2e5+10;
int n,K,A[N],B[N],t[N];
int lsh[N<<2],m;
const int M=N*30;
int ls[M],rs[M],rt[N],tot,sum[M];
inline void insert(int &p,int pre,int l,int r,int v){
	p=++tot; sum[p]=sum[pre]+1; ls[p]=ls[pre]; rs[p]=rs[pre];
	if(l==r) return ;
	int mid=(l+r)>>1;
	if(v<=mid) insert(ls[p],ls[pre],l,mid,v);
	else insert(rs[p],rs[pre],mid+1,r,v);
}
inline bool query(int p,int l,int r,int st,int ed){
	if(!p) return 0;
	if(st<=l&&r<=ed) return 1;
	int mid=(l+r)>>1;
	if(st<=mid&&query(ls[p],l,mid,st,ed)) return 1;
	return ed>mid&&query(rs[p],mid+1,r,st,ed);
}
inline int ask(int p,int l,int r,int st,int ed){
	if(!p) return 0;
	if(st<=l&&r<=ed) return sum[p];
	int mid=(l+r)>>1,res=0;
	if(st<=mid) res+=ask(ls[p],l,mid,st,ed);
	if(ed>mid) res+=ask(rs[p],mid+1,r,st,ed);
	return res;
}
signed main(){
	freopen("card.in","r",stdin); freopen("card.out","w",stdout);
	n=read(); K=read();
	ll ans=0;
	for(int i=1;i<=n;++i){
		A[i]=read(),B[i]=read();
		if(A[i]==B[i]) ans+=A[i],--i,--n;
	}
	rep(i,1,n) lsh[++m]=A[i],lsh[++m]=B[i];
	for(int i=1;i<=K;++i) t[i]=read(),lsh[++m]=t[i];
	sort(lsh+1,lsh+m+1);
	m=unique(lsh+1,lsh+m+1)-lsh-1;
	for(int i=K;i>=1;--i){
		t[i]=lower_bound(lsh+1,lsh+m+1,t[i])-lsh;
		insert(rt[i],rt[i+1],1,m,t[i]);	
	}
	for(int i=1;i<=n;++i){
		int l=1,r=K,pos=K+1;
		A[i]=lower_bound(lsh+1,lsh+m+1,A[i])-lsh;
		B[i]=lower_bound(lsh+1,lsh+m+1,B[i])-lsh;
		while(l<=r){
			int mid=(l+r)>>1;
			if(query(rt[mid],1,m,min(A[i],B[i]),max(A[i],B[i])-1)) l=mid+1;
			else pos=mid,r=mid-1;
		}
		int tim=ask(rt[pos],1,m,max(A[i],B[i]),m);
		if(pos==1){
			if(tim&1) ans+=lsh[B[i]];
			else ans+=lsh[A[i]];
		}else{
			if(tim&1) ans+=min(lsh[A[i]],lsh[B[i]]);
			else ans+=max(lsh[A[i]],lsh[B[i]]);
		}
	}
	print(ans);
	return 0;
}

让非最大值元素之间存在相对顺序,最后除 (n1)!

考虑使用容斥来处理每个元素出现次数不超过 K 的限制,枚举 n1 的整数划分,并钦定集合内部完全相等,集合之间不钦定不相等

fi 表示 i 个元素的集合的贡献系数,那么某种划分的容斥系数就是每个集合贡献系数并将集合无序化

gi 表示 i 个元素的集合的实际贡献系数,根据题意有 gi=[iK]i! ,对于一个划分数会将该块拆成若干个大小任意的块,这符合 ln 的计算定义,那么 EGF(fi)=ln(EGF(gi))

对于每个划分数求出来对应的方案数,继续使用平凡容斥去掉不超过 X 的限制,也就是先做一个简单的记录超过数字数量和集合奇偶性的 DP 算方案数

写出来一个大小为 siz 的集合在任意选择长度时在长度这维的 OGF:i0xi×siz=11xsiz 乘起来得到非最大值长度总和的 OGF

那么其指数 >L 的系数的和附加上选择最大值的方案 LY+1 可以直接贡献给答案,对于 Y<iL 的长度总和,其贡献系数为 (iY+1) 的形式,拆开之后可以求导再求系数和得到正确值

系数前缀和可以通过将函数 ×11x 得到

剩下的问题是求 [xn]f(x)g(x) ,使用 波斯坦-茉莉 算法解决,也就是说:

[xn]f(x)g(x)=[xn]f(x)g(x)f(x)g(x)

g(x)g(x) 只有偶数项系数非零,提取出来 f(x)g(x) 的奇数/偶数项讨论 n 的奇偶性变成子问题即可

计算时注意根据容斥原理指数发生了改变,那么对于 Y<xLx,贡献系数变成了 Ykx 的形式

Code Display
#define poly vector<int> 
const int N=110;
inline poly Mul(const poly &A,const poly &B){
	int n=A.size(),m=B.size();
	vector<int> c(n+m-1);
	for(int i=0;i<n;++i){
		for(int j=0;j<m;++j) ckadd(c[i+j],mul(A[i],B[j]));
	}
	return c;
}
inline poly deriv(const poly &a){
	int n=a.size();
	if(n==1) return {0};
	vector<int> b(n-1);
	for(int i=1;i<n;++i) b[i-1]=mul(a[i],i);
	return b;
}
int fac[N],ifac[N],inv[N],n,K,Y,X,L;
inline int Recurrence(vector<int> f,vector<int> g,int n){
	while(n){
		vector<int> tmp=g;
		for(int i=1;i<tmp.size();i+=2) tmp[i]=del(0,tmp[i]);
		f=Mul(f,tmp); g=Mul(g,tmp);
		int sizmx=-1;
		for(int i=(n&1);i<f.size();i+=2) f[i/2]=f[i],sizmx=i/2;
		f.resize(sizmx+1);
		sizmx=-1;
		for(int i=0;i<g.size();i+=2) g[i/2]=g[i],sizmx=i/2;
		g.resize(sizmx+1);
 		n>>=1;
	}
	if(f.size()) return mul(f[0],ksm(g[0],mod-2));
	else return 0;
}
int f[N],ton[N],dp[2][N],tmp[2][N];
inline int calc(vector<int> cur){
	int coef=1,sum=0;
	for(int t:cur) ckmul(coef,mul(f[t],ifac[t])),ton[t]++;
	rep(i,1,n-1) ckmul(coef,ifac[ton[i]]),ton[i]=0;
	vector<int> deno1={1};
	for(int t:cur){
		vector<int> tmp={1};
		for(int j=1;j<t;++j) tmp.emplace_back(0);
		tmp.emplace_back(mod-1);
		deno1=Mul(deno1,tmp);
	}
	memset(dp,0,sizeof(dp));
	dp[0][0]=1;
	for(int t:cur){
		rep(p,0,1) rep(j,0,n-1) if(dp[p][j]){
			ckadd(tmp[p][j],dp[p][j]);
			ckadd(tmp[p^1][j+t],dp[p][j]);
		}
		rep(p,0,1) rep(j,0,n-1) dp[p][j]=tmp[p][j],tmp[p][j]=0;
	}
	poly pre=Mul(deno1,{1,mod-1});
	poly nume2=Mul(deriv(deno1),{0,1});
	poly deno2=Mul(deno1,Mul(deno1,{1,mod-1}));
	for(int i=0;i<nume2.size();++i) nume2[i]=del(0,nume2[i]);

	for(int i=0;i<n;++i){
		if(dp[0][i]==dp[1][i]) continue;
		int res=0;
		int lef=L-i*(X+1),rig=n*L-i*(X+1);
		if(lef>=0){
			res-=Recurrence({1},pre,lef)*(L+1-Y)%mod;
		}
		if(rig>=0){
			res+=Recurrence({1},pre,rig)*(L+1-Y)%mod;
		}
		lef=Y-i*(X+1); rig=L-i*(X+1);
		if(rig>=0){
			res+=Recurrence(nume2,deno2,rig)-lef*Recurrence({1},pre,rig)%mod;
		}
		if(lef>=0){
			res-=Recurrence(nume2,deno2,lef)-lef*Recurrence({1},pre,lef)%mod;
		}
		res=(res%mod+mod)%mod;
		ckadd(sum,mul(res,del(dp[0][i],dp[1][i])));
	}
	return mul(coef,sum);
}
signed main(){
	freopen("dish.in","r",stdin); freopen("dish.out","w",stdout);
	n=100; fac[0]=inv[0]=1;
	for(int i=1;i<=n;++i) fac[i]=mul(fac[i-1],i);
	ifac[n]=ksm(fac[n],mod-2);
	for(int i=n;i>=1;--i) ifac[i-1]=mul(ifac[i],i),inv[i]=mul(ifac[i],fac[i-1]);
	n=read(); L=read(); Y=read(); X=read(); K=read();
	poly vec(K+1),pw={1};
	for(int i=1;i<=K;++i) vec[i]=1;
	for(int i=1;i<=n-1;++i){
		pw=Mul(pw,vec);
		if(pw.size()>n) pw.resize(n);
		for(int j=1;j<pw.size();++j){
			if(i&1) ckadd(f[j],mul(inv[i],pw[j]));
			else ckdel(f[j],mul(inv[i],pw[j]));
		}
	}
	rep(i,1,n-1) ckmul(f[i],fac[i]);
	int ans=0;
	function<void(int,int,vector<int>)>dfs=[&](const int rem,int lst,vector<int> cur){
		if(rem==0) return ckadd(ans,calc(cur));
		if(rem<lst) return ;
		for(int i=lst;i<=rem;++i){
			cur.emplace_back(i);
			dfs(rem-i,i,cur);
			cur.pop_back();
		}
		return ;
	};
	dfs(n-1,1,{});
	print(ans);
	return 0;
}

字符串

判定合法的过程可以通过维护一个栈来实现,如果栈顶和新加入的元素相同就弹栈否则压入,如果最后栈空了就合法

尝试分治:计算交换的两个元素跨过区间中点 mid 的数量,提前维护维护 [1,l),(r,n] 的压栈情况

判定最终合法的条件是 [1,mid] 入栈出栈的结果和 (mid+1,n] 的入栈出栈的结果为回文串

枚举左侧删掉字符的下标 i 以及替换成了哪个字符,算出来 [1,i],(i,mid] 的两个栈再算出来 [i1] 的栈和 [i+1,mid] 的栈的 lcp,剩下的元素哈希起来扔到哈希表里面

右边也枚举哪个元素被替换成了什么,答案的计算就是对于每种情况去哈希表里面查询

Code Display
const int N=1e5+10;
char s[N];
int n,a[N],ans;
ull pw[N],bas=13331;
struct Stack{
	int stk[N],top;
	ull hsl[N],hsr[N];
	
	inline void ins(int x){
		if(top&&x==stk[top]) --top;
		else{
			stk[++top]=x;
			hsl[top]=hsl[top-1]*bas+x;
			hsr[top]=hsr[top-1]+pw[top-1]*x;
		}
	}
	inline ull get_hs(int len){
		return hsl[top]-hsl[top-len]*pw[len];	
	}
}lef,rig,tmp;
inline int calc_lcp(Stack &a,Stack &b){
	int l=1,r=min(a.top,b.top),res=0;
	while(l<=r){
		int mid=(l+r)>>1;
		if(a.get_hs(mid)==b.get_hs(mid)) l=mid+1,res=mid;
		else r=mid-1;
	}
	return res;
}
inline void solve(int l,int r){
	if(l==r) return ;
	int mid=(l+r)>>1;
	rep(i,l,mid) lef.ins(a[i]);
	solve(mid+1,r);
	Down(i,mid,l) lef.ins(a[i]);
	Down(i,r,mid+1) rig.ins(a[i]);
	solve(l,mid);
	rep(i,l,mid) lef.ins(a[i]);
	tmp.top=0;
	unordered_map<ull,int> mp[4][4];
	for(int i=mid;i>=l;--i){
		lef.ins(a[i]);
		for(int t=1;t<=3;++t) if(a[i]!=t){
			lef.ins(t);
			int lcp=calc_lcp(lef,tmp);
			mp[a[i]][t][lef.hsl[lef.top-lcp]*pw[tmp.top-lcp]+tmp.hsr[tmp.top-lcp]]++;
			lef.ins(t);
		}
		tmp.ins(a[i]);	
	}
	tmp.top=0;
	for(int i=mid+1;i<=r;++i){
		rig.ins(a[i]);
		for(int t=1;t<=3;++t) if(a[i]!=t){
			rig.ins(t);
			int lcp=calc_lcp(rig,tmp);
			ull cur=rig.hsl[rig.top-lcp]*pw[tmp.top-lcp]+tmp.hsr[tmp.top-lcp];
			if(mp[t][a[i]].count(cur)) ans+=mp[t][a[i]][cur];
			rig.ins(t);
		}
		tmp.ins(a[i]);
	}
	return ;
}
signed main(){
	freopen("string.in","r",stdin); freopen("string.out","w",stdout);
	scanf("%s",s+1); n=strlen(s+1);
	pw[0]=1;
	rep(i,1,n){
		pw[i]=pw[i-1]*bas;	
		a[i]=s[i]-'a'+1;
	}
	solve(1,n);
	print(ans);
	return 0;
}

posted @   没学完四大礼包不改名  阅读(60)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示