luogu P6624 [省选联考 2020 A 卷] 作业题
题面传送门
这个东西一眼先把\(gcd\)提出来然后对于每个gcd只算权值为gcd倍数的答案。
然后这个东西简单容斥一下就是我们要的恰好为当前值的答案。
但是这个生成树权值和怎么做呢,矩阵树定理只能处理生成树权值积。
我们考虑魔改,如果我们将生成树权值积看成\(n-1\)个一次多项式相乘,然后取一次项,根据二项式定理这个东西很对。
只要我们能支持这个类型的四则运算即可。
加减直接对位相加减,乘法扔掉二次项,出发直接乘逆元即可。
然后还有一个问题就是我们没有办法每个权值做一次。
考虑一个生成树至少要有\(n-1\)条边,那么每个数拆出来只有\(144\)个约数,然后最多也久\(144\)左右的样子轻松可过。
时间复杂度\(O(n^3\sqrt w)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 30
#define M 200000
#define W (1<<20)
#define mod 998244353
#define eps (1e-5)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,k,x,y,z,Maxn,fa[N+5],cnt,un,wn,pus;ll G[M+5],Ans;
struct Edge{int x,y,z;}tmp;vector<Edge> Id[M+5];
I int Getfa(int x){return fa[x]==x?x:fa[x]=Getfa(fa[x]);}
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),y>>=1,x=x*x%mod;return ans;}
struct pai{
ll k,b;
pai operator +(const pai &s)const{return (pai){(k+s.k)%mod,(b+s.b)%mod};}
pai operator -(const pai &s)const{return (pai){(k-s.k+mod)%mod,(b-s.b+mod)%mod};}
pai operator *(const pai &s)const{return (pai){(k*s.b+b*s.k)%mod,b*s.b%mod};}
pai operator /(const pai &s)const{ll inv=mpow(s.b);return (pai){((k*s.b-b*s.k)%mod+mod)*inv%mod*inv%mod,b*inv%mod};}
}A[N+5][N+5],now,Mod,Cl={0,1};
I void insert(int x,int y,int z){A[x][x].k+=z;A[x][x].b++;A[y][y].k+=z;A[y][y].b++;A[x][y].k-=z;A[x][y].b--;A[y][x].k-=z;A[y][x].b--;}
I ll calc(){
re int i,j,h;pai Ans=Cl;for(i=1;i<n;i++){
if(!A[i][i].b){for(pus=0,j=i+1;j<=n;j++) A[j][i].b&(pus=j);swap(A[i],A[pus]);Ans=Mod-Ans;}
for(j=i+1;j<n;j++){
now=A[j][i]/A[i][i];for(h=i;h<n;h++) A[j][h]=A[j][h]-now*A[i][h];
}Ans=Ans*A[i][i];
}return Ans.k;
}int main(){
freopen("1.in","r",stdin);
re int i,j,h;scanf("%d%d",&n,&m); for(i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);Maxn=max(Maxn,z);for(j=1;j*j<=z;j++) !(z%j)&&(Id[j].push_back((Edge){x,y,z}),(j*j)^z&&(Id[z/j].push_back((Edge){x,y,z}),0));
}for(i=Maxn;i;i--){
for(j=1;j<=n;j++) fa[j]=j;for(cnt=n,j=0;j<Id[i].size();j++)tmp=Id[i][j],un=Getfa(tmp.x),wn=Getfa(tmp.y),un^wn&&(fa[un]=wn,cnt--);
/*if(i==10080) printf("%d %d\n",cnt,Id[i].size());*/if(cnt^1) continue;for(j=1;j<=n;j++) for(h=1;h<=n;h++) A[j][h]=Mod;for(j=0;j<Id[i].size();j++) tmp=Id[i][j],insert(tmp.x,tmp.y,tmp.z);
G[i]=calc();/*printf("%lld %d\n",G[i],i);*/for(j=2*i;j<=Maxn;j+=i) G[i]-=G[j];G[i]=(G[i]%mod+mod)%mod;Ans+=G[i]*i%mod;
}printf("%lld\n",Ans%mod);
}