省选测试41

省选测试41

总结

\(T1,T3\) 都是推式子的题,\(T2\) 是一个套路题,之前没有见过。

A. 多边形

分析

\(k>3\) 时无解,因为多边形外角和为\(360\)。一个锐角对应一个\(>90\)的外角,故最多\(3\)个。

对于 \(k \leq 3\) 的情况大力分类讨论即可。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=1e6+5,mod=1000109107;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
int t,n,m,k,jc[maxn],jcc[maxn],ny[maxn];
void pre(){
	ny[1]=1;
	for(rg int i=2;i<maxn;i++) ny[i]=mulmod(mod-mod/i,ny[mod%i]);
	jc[0]=jcc[0]=1;
	for(rg int i=1;i<maxn;i++) jc[i]=mulmod(jc[i-1],i),jcc[i]=mulmod(jcc[i-1],ny[i]);
}
int getC(rg int nn,rg int mm){
	if(nn<mm) return 0;
	return mulmod(jc[nn],mulmod(jcc[mm],jcc[nn-mm]));
}
int getsum(rg int now){
	return mulmod(now,mulmod(now+1,ny[2]));
}
int solve3(){
	if(m!=3) return 0;
	else return delmod(getC(n,m),mulmod(n,getsum(n/2-1)));
}
int solve2(){
	if(m==3) return mulmod(n,getsum(n/2-1));
	else return mulmod(n,addmod(mulmod(2,getC(n/2,m-1)),getC(n/2,m-2)));
}
int solve1(){
	rg int ans=addmod(mulmod(getC(n/2,m-2),n/2),getC(n/2,m-1));
	ans=delmod(mulmod(ans,n),addmod(mulmod(2,solve2()),mulmod(3,solve3())));
	return ans;
}
int solve0(){
	return delmod(getC(n,m),addmod(solve1(),addmod(solve2(),solve3())));
}
int main(){
	pre();
	read(t);
	while(t--){
		read(n),read(m),read(k);
		if(k>=4) printf("0\n");
		else if(k==3) printf("%d\n",solve3());
		else if(k==2) printf("%d\n",solve2());
		else if(k==1) printf("%d\n",solve1());
		else printf("%d\n",solve0());
	}
	return 0;
}

B. 仙人掌

分析

什么乱七八糟的仙人掌,就是一棵树。

对于每一个点开一颗 \(trie\) 树维护子树内的异或和,支持整体加一、删除一个数、加入一个数。

由低位到高位建 \(trie\),整体加一的时候相当于把最低位的一个 \(0\) 变成 \(1\),把后面所有的 \(1\) 都翻转成 \(0\)

具体实现的时候就是交换左右儿子,递归 \(0\) 的那一部分子树。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=1e6+5,mod=1000109107;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
int h[maxn],tot=1,n,m,tag[maxn],a[maxn],fa[maxn],ans;
struct asd{
	int to,nxt;
}b[maxn];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
void dfs(rg int now,rg int lat){
	fa[now]=lat;
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs(u,now);
	}
}
int rt[maxn],ch[maxn*10][2],sum[maxn*10],num[maxn*10],cnt;
void push_up(rg int da,rg int bit){
	num[da]=num[ch[da][0]]+num[ch[da][1]];
	sum[da]=sum[ch[da][0]]^sum[ch[da][1]];
	if(num[ch[da][1]]&1) sum[da]^=(1<<(bit-1));
}
void insert(rg int &da,rg int val,rg int bit){
	if(!da) da=++cnt;
	if(bit>20){
		num[da]++;
		return;
	}
	if(val&(1<<(bit-1))) insert(ch[da][1],val,bit+1);
	else insert(ch[da][0],val,bit+1);
	push_up(da,bit);
}
void del(rg int da,rg int val,rg int bit){
	if(bit>20){
		num[da]--;
		return;
	}
	if(val&(1<<(bit-1))) del(ch[da][1],val,bit+1);
	else del(ch[da][0],val,bit+1);
	push_up(da,bit);
}
void xg(rg int da,rg int bit){
	if(bit>20) return;
	std::swap(ch[da][0],ch[da][1]);
	if(ch[da][0]) xg(ch[da][0],bit+1);
	push_up(da,bit);
}
int getans(rg int now){
	rg int nans=0;
	if(fa[now]) nans^=(a[fa[now]]+tag[fa[fa[now]]]);
	nans^=sum[rt[now]];
	return nans;
}
int main(){
	memset(h,-1,sizeof(h));
	read(n),read(m);
	rg int aa,bb;
	for(rg int i=1;i<n;i++){
		read(aa),read(bb);
		ad(aa,bb),ad(bb,aa);
	}
	dfs(1,0);
	for(rg int i=1;i<=n;i++) if(fa[i]) insert(rt[fa[i]],0,1);
	rg int nans=0;
	for(rg int i=1;i<=m;i++){
		read(aa);
		tag[aa]++;
		if(fa[aa]){
			if(fa[fa[aa]]) del(rt[fa[fa[aa]]],a[fa[aa]]+tag[fa[fa[aa]]],1);
			a[fa[aa]]++;
			if(fa[fa[aa]]) insert(rt[fa[fa[aa]]],a[fa[aa]]+tag[fa[fa[aa]]],1);
		}
		xg(rt[aa],1);
		nans=getans(aa);
		ans=addmod(ans,mulmod(nans,addmod(mulmod(i,i),i)));
	}
	printf("%d\n",ans);
	return 0;
}

C. 多项式

分析

首先把题目中给的公式写出来,这些公式推导的时候会用到。

\(x^{\underline{n}}=\sum_{k=0}^n(-1)^{n-k}s(n,k)x^k\)

\(x^n=\sum_{k=0}^nS(n,k)x^{\underline{k}}\)

\(\Delta^n f(x) \sum_{i=0}^n(-1)^{n-i}\binom{n}{i}f(x+i)\)

\(\Delta^n f(x) \sum_{i=0}^dc_i\binom{x}{n-i},f(x)=\sum_{i=0}^dc_i\binom{x}{i}\)

考虑二项式反演,设 \(g[k]\) 为钦定前 \(n\) 个变量中有 \(k\) 个变量 \(>t\) 的方案数。

那么 \(g[k]=\binom{n}{k}\binom{s-kt}{m}\)

含义就是从前面的 \(n\) 个位置中选出 \(k\) 个不合法的,那么还剩下 \(s-kt\) 个数分给 \(m\) 个位置,每一个位置上不能为空,数可以不放完。

用插板法,新加入一个集合存放多余的元素,再新加一个元素分给这个集合。

根据二项式反演,

\(ans=\sum_{i=0}^n(-1)^i\binom{n}{i}\binom{s-it}{m}\)

\(i=n-i\),则

\(ans=\sum_{i=0}^n(-1)^{n-i}\binom{n}{i}\binom{s-nt+it}{m}\)

\(f(x)=\binom{s-nt+xt}{m}\)

那么根据题目中的第三个式子

\(\Delta^n f(x)=\sum_{i=0}^n(-1)^{n-i} \binom{n}{i}\binom{m}{s-nt+xt+it}\)

同时根据第四个式子,

\(\Delta^n f(0)=\sum_{i=0}^mc_i\binom{i-n}{0}\)

注意到只有 \(\binom{0}{0}\) 才有值,所以 \(\Delta^n f(0)=c_n\)

所以只要求出 \(c_n\) 的值即可。

根据最后一个式子,\(f(x)=\sum_{i=0}^mc_i\binom{x}{i}\)

暴力展开 \(f(x)=\sum_{i=0}^mc_i \frac{x^{\underline{i}}}{i!}\)

仍然使用题目中的良心公式,

\(f(x)=\sum_{i=0}^m\frac{c_i}{i!} \sum_{j=0}^i(-1)^{i-j}s(i,j)x^j\)

同时根据我们最开始的定义式,

\(f(x)=\binom{s-nt+xt}{m}=\frac{1}{m!}(s-nt+xt)^{\underline{m}}=\frac{1}{m!}\sum_{i=0}^m(-1)^{m-i}s(m,i)(s-nt+xt)^i\)

对于任意 \(x^k\) 项,这两个式子的系数都是相同的

所以

\(\sum_{i=k}^m\frac{c_i}{i!}(-1)^{i-k}s(i,k)x^k=\frac{1}{m!}\sum_{i=k}^m(-1)^{m-i}s(m,i)\binom{i}{k}t^k(s-nt)^{i-k}x^k\)

对于这个式子可以依次求出 \(c_m\)\(c_n\) 的值,然后回代

对于两个阶乘,可以把 \(\frac{1}{m!}\) 乘到右边,因为 \(m-i\) 不会很大,所以可以直接算

对于组合数,可以预处理出一列,然后去递推

对于第一类斯特林数,我们会发现 \(n-m\) 很小,也就是说有很多个环的大小都为 \(1\),我们可以去枚举有哪些环的大小大于 \(1\)

那么就是 \(\sum_{i=0}^{n-m}\binom{n}{m-i}ss(n-m+i,i)\)

其中 \(ss(n,m)\) 表示将 \(n\) 个元素排成 \(m\) 个环,环的大小至少为 \(2\) 的方案数

递推式为 \(ss(n,m)=(n-1)ss(n-1,m)+(n-1)ss(n-2,m-1)\)

实现的时候要精细一下。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<iostream>
#define rg register
template<typename T>void read(rg T& x){
	x=0;rg int fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=fh;
}
const int maxn=2e3+5,mod=1000109107;
inline int addmod(rg int now1,rg int now2){
	return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
	return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
	return now1*=now2,now1>=mod?now1%mod:now1;
}
int ksm(rg int ds,rg int zs){
	rg int nans=1;
	while(zs){
		if(zs&1) nans=mulmod(nans,ds);
		ds=mulmod(ds,ds);
		zs>>=1;
	}
	return nans;
}
int n,m,t,ans,c[maxn][maxn],prestr[maxn][maxn],str[maxn][maxn],jc[maxn],jcc[maxn],ny[maxn],beg;
long long s;
int getC(rg int nn,rg int mm){
	if(nn<mm || nn<0 || mm<0) return 0;
	return c[nn-beg][mm-beg];
}
int getpres(rg int nn,rg int mm){
	rg int nans=0;
	for(rg int i=0;i<=nn-mm;i++){
		nans=addmod(nans,mulmod(getC(nn,mm-i),prestr[nn-mm+i][i]));
	}
	return nans;
}
int getS(rg int nn,rg int mm){
	return str[nn-n][mm-n];
}
void pre(){
	ny[1]=1;
	for(rg int i=2;i<maxn;i++) ny[i]=mulmod(mod-mod/i,ny[mod%i]);
	jc[0]=jcc[0]=1;
	for(rg int i=1;i<maxn;i++){
		jc[i]=mulmod(jc[i-1],i);
		jcc[i]=mulmod(jcc[i-1],ny[i]);
	}
	beg=std::max(0,n-1000);
	c[0][0]=1;
	for(rg int i=1;i<maxn;i++){
		c[i][0]=mulmod(c[i-1][0],mulmod(beg+i,ny[i]));
	}
	for(rg int i=1;i<maxn;i++){
		for(rg int j=1;j<=i;j++){
			c[i][j]=addmod(c[i-1][j],c[i-1][j-1]);
		}
	}
	prestr[0][0]=1;
	for(rg int i=2;i<maxn;i++){
		for(rg int j=1;j<=i;j++){
			prestr[i][j]=addmod(mulmod(i-1,prestr[i-2][j-1]),mulmod(i-1,prestr[i-1][j]));
		}
	}
	str[0][0]=getpres(n,n);
	for(rg int i=1;i<=m-n;i++){
		str[i][0]=getpres(n+i,n);
	}
	for(rg int i=1;i<=m-n;i++){
		for(rg int j=1;j<=i;j++){
			str[i][j]=addmod(str[i-1][j-1],mulmod(i+n-1,str[i-1][j]));
		}
	}
}
int xs[maxn];
void solve(){
	rg int tmp;
	for(rg int i=m;i>=n;i--){
		tmp=1;
		for(rg int j=m;j>=i+1;j--){
			xs[i-n]=delmod(xs[i-n],mulmod(xs[j-n],mulmod(tmp,mulmod(getS(j,i),ksm(mod-1,j-i)))));
			tmp=mulmod(tmp,j);
		}
		for(rg int j=i;j<=m;j++){
			xs[i-n]=addmod(xs[i-n],mulmod(ksm(mod-1,m-j),mulmod(getS(m,j),mulmod(getC(j,i),mulmod(ksm(t,i),ksm((s-1LL*n*t)%mod,j-i))))));
		}
		tmp=1;
		for(rg int j=i+1;j<=m;j++) tmp=mulmod(tmp,ksm(j,mod-2));
		xs[i-n]=mulmod(xs[i-n],tmp);
	}
}
int main(){
	read(s),read(t),read(n),read(m);
	pre();
	solve();
	printf("%d\n",xs[0]);
	return 0;
}
posted @ 2021-03-25 19:37  liuchanglc  阅读(74)  评论(0编辑  收藏  举报