T1:一道很妙的综合题
首先显然按位做
然后边权只有0/1/2
首先我们把一棵生成树的权值定义为边权之积
(边权可以看做有这么多条重边)
那么我们就可以用矩阵树定理求出所有生成树的权值的积
(
矩阵树定理就是用来解决生成树计数的
先去自环
构造如下矩阵
g[i][i]=i的度数
如果g[i][j]有一条边,那么g[i][j]=-1
求出它的行列式就是生成树数量
)
但是我们要求的是边权和
那么我们把边权看做一个多项式
乘起来就可以达到指数相加的效果了
把边权看做多项式的指数
构造出对应的矩阵,其中每个元素都是多项式
用矩阵树定理就可以求出答案多项式
系数即为权值和为指数的方案数
但是这样我们就要写一堆奇怪的多项式运算,复杂度O(n^5*log)
(你可以用FFT)
FFT并没有什么卵用
我们发现生成树的边权和最大为2*(n-1)
于是我们就知道了答案多项式的最高次
我们用点值表示,用矩阵树定理求出0-2*n的点带入后的值
然后拉格朗日插值法求出答案多项式即可
于是复杂度就只有O(n^4*log)
我的插值写成O(n^3)了虽然这题没什么关系
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const ll maxn=105,maxm=maxn*maxn,mod=1e9+7; using namespace std; struct Edge{int u,v,l;}E[maxm]; int n,m;ll g[maxn][maxn],ans,down[maxn]; struct poly{ ll a[maxn],len; void clear(){memset(a,0,sizeof(a)),len=0,a[0]=1;} ll &operator [] (const ll &x){return a[x];} void watch(){ for (ll i=len;i>=0;i--) printf("%d ",a[i]);puts(""); } poly operator *(poly b){ poly res;res.len=len+b.len; memset(res.a,0,sizeof(res.a)); for (ll i=0;i<=len;i++) for (ll j=0;j<=b.len;j++) res[i+j]=(res[i+j]+1ll*a[i]*b.a[j]%mod)%mod; return res; } }ori,f,pre[maxn],h,back[maxn]; void read(ll &x){ char ch; for (ch=getchar();!isdigit(ch);ch=getchar()); for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; } ll qpow(ll a,ll b){ ll res=1; for (;b;b>>=1,a=1ll*a*a%mod) if (b&1) res=1ll*res*a%mod; return res; } ll inv(ll x){ return qpow(x,mod-2); } ll det(ll n){ ll res=1; for (ll i=1,j;i<=n;i++){ for (j=i;j<=n&&!g[j][i];j++); if (j>n) return 0; if (j!=i){ res*=-1; for (ll k=1;k<=n;k++) swap(g[i][k],g[j][k]); } for (ll j=i+1;j<=n;j++){ ll tmp=1ll*inv(g[i][i])*g[j][i]%mod; for (ll k=i;k<=n;k++) g[j][k]=(g[j][k]-1ll*tmp*g[i][k]+mod)%mod; } res=1ll*res*g[i][i]%mod; } return (res+mod)%mod; } void prework(){ pre[0].len=1,pre[0][0]=0,pre[0][1]=1; for (ll i=1;i<=2*n;i++){ pre[i].clear(); poly tmp; tmp[1]=1,tmp[0]=-i,tmp.len=1; pre[i]=pre[i-1]*tmp; } // pre[1].watch(); // pre[2].watch(); // pre[3].watch(); back[2*n+1].clear(); for (ll i=2*n;i>=0;i--){ back[i].clear(); poly tmp; tmp[1]=1,tmp[0]=-i,tmp.len=1; back[i]=back[i+1]*tmp; } // puts("%p"); back[2*n].watch(); // back[2*n-1].watch(); } ll solve(ll pw){ for (ll i=0;i<=2*n;i++){ memset(g,0,sizeof(g)); for (ll j=1;j<=m;j++){ ll u=E[j].u,v=E[j].v,val=qpow(i,E[j].l/pw%3); g[u][v]-=val,g[v][u]-=val,g[u][u]+=val,g[v][v]+=val; } /*if (pw==1) for (ll j=1;j<=n;j++,puts("")) for (ll k=1;k<=n;k++) printf("%d ",g[j][k]);*/ f[i]=det(n-1); //printf("f=%d\n",f[i]); } //puts(""); for (ll i=0;i<=2*n;i++) h.a[i]=0; for (ll i=0;i<=2*n;i++){ ll down=1; for (ll j=0;j<=2*n;j++) if (i!=j) down=1ll*down*(i-j)%mod; down=1ll*inv(down)*f[i]%mod; poly up; if (i==0) up=back[1]; else up=pre[i-1]*back[i+1]; //printf("%d::",i),up.watch(); //if (pw==1) printf("down=%d\n",down); for (ll j=2*n;j>=0;j--){ h[j]=(h[j]+1ll*up[j]*down%mod)%mod; //printf("up=%d fx=%d h=%d\n",up[j],down,h[j]); } } ll res=0;//puts("modp"); for (ll i=0;i<=2*n;i++){ //printf("h=%d\n",h[i]); res=(res+1ll*(i%3)*h[i])%mod; } return (res+mod)%mod; } int main(){ //freopen(".in","r",stdin);freopen(".out","w",stdout); scanf("%d%d",&n,&m); for (ll i=1;i<=m;i++) scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].l); prework(); for (ll pw=1,i=0;i<9;i++) ans=(ans+1ll*pw*solve(pw))%mod,pw*=3; printf("%lld\n",ans); return 0; }
T3:原题不解释
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> typedef long long ll; const int maxn=2010; using namespace std; int n,mod; ll f[maxn][maxn],g[maxn][maxn],c[maxn][maxn],pw[maxn*maxn]; void read(int &x){ char ch; for (ch=getchar();!isdigit(ch);ch=getchar()); for (x=0;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; } void getc(){ c[0][0]=1; for (int i=1;i<=n;i++){ c[i][0]=1; for (int j=1;j<=i;j++){ c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod; //printf("%d ",c[i][j]); } } } void getg(){ g[0][0]=1; for (int i=1;i<=n;i++){ int m=i*(i-1)/2%mod; g[i][0]=pw[m],g[i][1]=1ll*m*pw[m-1]%mod; for (int j=0;j<i;j++) g[i][2]=(g[i][2]+(1ll*c[i-1][j]*((g[i-1][2]+2ll*g[i-1][1]*j%mod+1ll*j*j%mod*g[i-1][0]%mod))%mod)%mod)%mod; //cout<<g[i][0]<<' '<<g[i][1]<<' '<<g[i][2]<<endl; } } void getf(){ memcpy(f,g,sizeof(f)); for (int i=1;i<=n;i++){ for (int j=1;j<i;j++){ f[i][0]=(f[i][0]-1ll*c[i-1][j-1]*f[j][0]%mod*g[i-j][0]%mod+mod)%mod; f[i][1]=(f[i][1]-1ll*c[i-1][j-1]*(1ll*f[j][0]*g[i-j][1]%mod+1ll*f[j][1]*g[i-j][0]%mod)%mod+mod)%mod; f[i][2]=(f[i][2]-1ll*c[i-1][j-1]*(1ll*f[j][0]*g[i-j][2]%mod+2ll*f[j][1]*g[i-j][1]%mod+1ll*f[j][2]*g[i-j][0]%mod)%mod+mod)%mod; } //printf("%d %d %d\n",f[i][0],f[i][1],f[i][2]); } } int main(){ scanf("%d%d",&n,&mod); pw[0]=1;for (int i=1;i<=n*n;i++) pw[i]=(pw[i-1]<<1)%mod; getc(),getg(),getf(); cout<<f[n][2]<<endl; return 0; } //2000 989184000 //2000 1000000000