省选测试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;
}