专项测试(数学4)
今天一不小心暴力没打完哈!
就被爆杀了,某某人比我高30分
这两天考的好难,我只能打暴力诶。
T1 传统题
这个??好像对我的组合能力有那么一点点的提升!!!
在考场上我想到了容斥,我是用至少形式转化的
然而至少形式使得容斥变得非常困难,需要考虑各种因素,而且可能会导致复杂度的升高。
于是我打了个小\(DP\)就走了
发现至少形式和至多形式可以互相转换,用总方案数减去至多为\(x\)的就是至少为\(x+1\)的
那么我们可以容斥这个至多,这样的话,我们只需要选出几个超了的就好了
这题最重要的一点就是组合含义的转化
我们可以强行给一些式子赋予一个组合含义
有时候赋予之后就可能降低复杂度
这里的组合含义转化是依靠特殊点来转化的,染色也是很重要的一点
对于染色有一个技巧,如果要选出来几个数染成\(m-1\)中颜色,我们可以认为不染是第\(m\)种颜色,这样就不需要枚举选几个了
还有一个技巧,当一个东西不太好直接组合数求的时候,我们可以尝试枚举一些值
这样把一些东西分的清清楚楚就能使求解更加容易,我们可以后面在把一些枚举合并
分析复杂度也很重要,比如两个值之间有大小的限制!!!
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int N=3e5+5;
int n,m,mod,ans;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int jc[N],inv[N];
int C(int x,int y){return jc[x-1]*inv[y]%mod*inv[x-y]%mod;}
int pm[N],pmj[N];
int S(int t,int k){
return (k*pmj[k-1]%mod*pm[n-t-k]+(n-t-k>0?(n-t-k)*pmj[k]%mod*pm[n-t-k-1]:0))%mod;
}
signed main(){
n=read();m=read();mod=read();
jc[0]=1;fo(i,1,n)jc[i]=jc[i-1]*i%mod;
inv[0]=1;inv[n]=ksm(jc[n],mod-2);
fu(i,n-1,1)inv[i]=inv[i+1]*(i+1)%mod;
pm[0]=1;fo(i,1,n)pm[i]=pm[i-1]*m%mod;
pmj[0]=1;fo(i,1,n)pmj[i]=pmj[i-1]*(m-1)%mod;
fo(i,0,n-1)fo(k,0,n){
if(i*k+k>n)break;
int res=C(n-i*k,k)*S(i*k,k)%mod;
if(k&1)ans=(ans-res+mod)%mod;
else ans=(ans+res)%mod;
}
ans=(n*ksm(m,n)%mod-ans*m%mod+mod)%mod;
printf("%lld",ans);
return 0;
}
T2 生成树
这我考场上一直想\(meet\ in\ the\ middle\)
于是我没了,状压打了一年发现记重了......
想到是矩阵树定理,然而没想到怎么做
最后是设权值,然后用二维拉格朗日插系数
设绿色和蓝色边的权值分别为\(x,y\)
这样我们去求行列式,得到的权值就是一个二维多项式\(a_{i,j}x^iy^j\)的答案
而指数\(i,j\)就是绿色和蓝色边的数量,可以找到\(n^2\)个点值
拉格朗日二维和屌丝消元都行
\(x,y\)是变量,\(z\)是点值,拉格朗日二维的公式
我懒了,写的屌丝消元
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
random_device you;
mt19937 pyt(you());
int rd(int l,int r){
uniform_int_distribution<> ee(l,r);
return ee(pyt);
}
const int mod=1e9+7;
const int N=45;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,m,g,b,ans;
int e[N][N][3];
struct matrix_tree{
int a[N][N];
void add(int x,int y,int v){
a[x][x]=(a[x][x]+v)%mod;
// a[y][y]=(a[y][y]+v)%mod;
a[x][y]=(a[x][y]-v+mod)%mod;
// a[y][x]=(a[y][x]-v+mod)%mod;
}
void init(int x,int y){
memset(a,0,sizeof(a));
fo(i,1,n)fo(j,1,n){
add(i,j,1*e[i][j][0]);
add(i,j,x*e[i][j][1]%mod);
add(i,j,y*e[i][j][2]%mod);
}
}
int ds(){
int ret=1;
fo(i,2,n){
int mx=i;
fo(j,i+1,n)if(a[j][i]>a[i][i]){
fo(k,i,n)swap(a[i][k],a[j][k]);
ret=(mod-ret)%mod;break;
}
fo(j,i+1,n){
int iv=a[j][i]*ksm(a[i][i],mod-2)%mod;
fo(k,i,n)a[j][k]=(a[j][k]-a[i][k]*iv%mod)%mod;
}
}
fo(i,2,n)ret=ret*a[i][i]%mod;
return (ret+mod)%mod;
}
int hls(int x,int y){
init(x,y);return ds();
}
}mt;
int a[N*N][N*N],x[N*N];
int id[N][N],cnt;
void gs(){
fo(i,1,cnt){
int mx=i;
fo(j,i+1,cnt)if(a[j][i]>a[i][i]){
fo(k,i,cnt+1)swap(a[i][k],a[j][k]);
break;
}
if(!a[i][i])continue;
fo(j,i+1,cnt){
int iv=a[j][i]*ksm(a[i][i],mod-2)%mod;
fo(k,i,cnt+1)a[j][k]=(a[j][k]-a[i][k]*iv%mod+mod)%mod;
}
}
fu(i,cnt,1){
int t=a[i][cnt+1];
fo(j,i+1,cnt)t=(t-x[j]*a[i][j]%mod+mod)%mod;
x[i]=t*ksm(a[i][i],mod-2)%mod;
}
}
signed main(){
n=read();m=read();g=read();b=read();
fo(i,1,m){
int x=read(),y=read(),z=read()-1;
e[x][y][z]++;e[y][x][z]++;
}
fo(i,0,n-1)fo(j,0,n-1-i)id[i][j]=++cnt;
fo(t,1,cnt){
int x=rd(1,mod-1),y=rd(1,mod-1);
fo(i,0,n-1){
int now=ksm(x,i);
fo(j,0,n-1-i)a[t][id[i][j]]=now,now=now*y%mod;
}
a[t][cnt+1]=mt.hls(x,y);
}gs();
fo(i,0,g)fo(j,0,b){
ans=(ans+x[id[i][j]])%mod;
}
printf("%lld",ans);
return 0;
}
T3 最短路径
这我只会树的分
要是基环的话......
基环树比树恶心一万倍......
分治!直接把环断开,不用倍长
发现中点两侧内部连边不可能绕一圈,于是这是第一个分治
要是跨越连边,比如说分成了四块,1和4连边,2和3连边
那就直接计算,这时我们发现好像1和3,2和4连边的话好像无法处理
其实我们把1和3拎出来合成一个块,就可以看做整块处理了,继续1和4,2和3连边就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int read(){
int s=0,t=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')t=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
return s*t;
}
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=998244353;
const int N=1<<18;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int n,m,an[N],ans;
struct NTT{
int af[N],g[N],len,lim;
void ntt(int *a,int lim){
fo(i,0,lim-1)if(af[i]>i)swap(a[i],a[af[i]]);
for(int t=lim>>1,d=1;d<lim;d<<=1,t>>=1)
for(int i=0;i<lim;i+=(d<<1))
fo(j,0,d-1){
int tmp=g[t*j]*a[i+j+d]%mod;
a[i+j+d]=(a[i+j]-tmp+mod)%mod;
a[i+j]=(a[i+j]+tmp)%mod;
}
}
int a[N],b[N];
void mul(int *s,int *t,int ls,int lt,int tp,int py){
for(lim=1,len=0;lim<=ls+lt;lim<<=1,len++);
fo(i,0,lim-1)af[i]=(af[i>>1]>>1)|((i&1)<<(len-1));
fo(i,0,lim-1)a[i]=s[i],b[i]=t[i];
g[0]=1;g[1]=ksm(3,(mod-1)/lim);
fo(i,2,lim-1)g[i]=g[i-1]*g[1]%mod;
ntt(a,lim);ntt(b,lim);
g[0]=1;g[1]=ksm(g[1],mod-2);
fo(i,2,lim-1)g[i]=g[i-1]*g[1]%mod;
fo(i,0,lim-1)a[i]=a[i]*b[i]%mod;
ntt(a,lim);int iv=ksm(lim,mod-2);
fo(i,0,ls+lt)an[i+py]=(an[i+py]+a[i]*iv%mod*tp%mod)%mod;
}
}nt;
struct E{int to,nxt;}e[N];
int head[N],rp=1;
void add_edg(int x,int y){e[++rp].to=y;e[rp].nxt=head[x];head[x]=rp;}
int siz[N],ms[N],mx,rt;bool vis[N],cir[N],vie[N];
void get_rt(int x,int f,int sz){
siz[x]=1;ms[x]=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==f||vis[y]||cir[y])continue;
get_rt(y,x,sz);siz[x]+=siz[y];ms[x]=max(ms[x],siz[y]);
}
ms[x]=max(ms[x],sz-siz[x]);if(mx>ms[x])rt=x,mx=ms[x];
}
int up[N],dw[N],mxd,md;
void get_dis(int x,int f,int d){
dw[d]++;md=max(md,d);
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==f||vis[y]||cir[y])continue;
get_dis(y,x,d+1);
}
}
void sol(int x){mxd=0;up[0]=1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(vis[y]||cir[y])continue;
md=0;get_dis(y,x,1);nt.mul(dw,dw,md,md,mod-1,0);
fo(j,0,md)up[j]=(up[j]+dw[j])%mod,dw[j]=0;mxd=max(mxd,md);
}
nt.mul(up,up,mxd,mxd,1,0);fo(i,0,mxd)up[i]=0;
}
void cal(int x,int sz){
vis[x]=true;sol(x);
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(vis[y]||cir[y])continue;mx=inf;
if(siz[y]<siz[x])get_rt(y,x,siz[y]),cal(rt,siz[y]);
else get_rt(y,x,sz-siz[x]),cal(rt,sz-siz[x]);
}
}
void spj1(){
mx=inf;get_rt(1,0,n);cal(rt,n);
int iv2=ksm(2,mod-2);
fo(i,1,n)an[i]=an[i]*iv2%mod;
}
vector<int> son[N];
void work(int l1,int r1,int l2,int r2,int x){
mxd=0;md=0;
fo(i,l1,r1){
fo(j,0,(int)son[i].size()-1)up[j+r1-i]+=son[i][j];
mxd=max(mxd,(int)son[i].size()+r1-i);
}
fo(i,l2,r2){
fo(j,0,(int)son[i].size()-1)dw[j+i-l2]+=son[i][j];
md=max(md,(int)son[i].size()+i-l2);
}
nt.mul(up,dw,mxd,md,1,x);
fo(i,0,mxd)up[i]=0;
fo(i,0,md)dw[i]=0;
}
void sol1(int l,int r){
if(l==r)return ;int mid=l+r>>1;
work(l,mid,mid+1,r,1);
sol1(l,mid);sol1(mid+1,r);
}
void sol2(int l1,int r1,int l2,int r2,int x){
if(l1==r1||l2==r2)return work(l1,r1,l2,r2,x),void();
int m1=l1+r1>>1,m2=l2+m1-l1;work(m1+1,r1,l2,m2,x);
sol2(l1,m1,l2,m2,r1-m1+x);
sol2(m1+1,r1,m2+1,r2,m2+1-l2+x);
}
int fa[N],c[N],cr;
void dfs_cir(int x,int f){
vis[x]=true;fa[x]=f;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(vie[i])continue;vie[i]=vie[i^1]=true;
if(y==f)continue;
if(vis[y]){cir[x]=true;cir[fa[y]]=true;continue;}
dfs_cir(y,x);cir[x]^=cir[y];
}vis[x]=false;
if(cir[x])c[++cr]=x;
}
void dfs_siz(int x,int f){
siz[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==f||cir[y])continue;
dfs_siz(y,x);siz[x]+=siz[y];
}
}
void spj2(){
dfs_cir(1,0);
fo(i,1,cr){
dfs_siz(c[i],0);
md=0;get_dis(c[i],0,0);
fo(j,0,md)son[i].push_back(dw[j]),dw[j]=0;
mx=inf;cir[c[i]]=false;get_rt(c[i],0,siz[c[i]]);
cal(rt,siz[c[i]]);cir[c[i]]=true;
}
int iv2=ksm(2,mod-2);
fo(i,1,n)an[i]=an[i]*iv2%mod;
int mid=cr>>1;
sol1(1,mid);sol1(mid+1,cr);
sol2(1,mid,mid+1,mid*2,1);
sol2(mid+2,cr,1,cr-mid-1,1);
}
signed main(){
n=read();m=read();
bool flag=false;
fo(i,1,n){
int x=read(),y=read();
if(x==y){flag=true;continue;}
add_edg(x,y);add_edg(y,x);
}
if(flag)spj1();
else spj2();
fo(i,1,n)ans=(ans+an[i]*ksm(i,m))%mod;
ans=ans*ksm(n*(n-1)/2%mod,mod-2)%mod;
printf("%lld",ans);
}