联合省选2020 题解

Day1T1

  • \(pre_{0/1},suf_{0/1}\)分别表示两种战士的能量前/后缀和
  • 要求 \(\min\{pre_0[i],suf_1[i]\}\),而 \(suf_1[i]=sum_1-pre_1[i]+val_1[i]\)
  • 为了常数小我们考虑树状数组。
  • 首先对温度离散化,因为答案一定是某个战士的温度(即在 \(x\) 中出现过的)。
  • 然后,我们从前往后考虑每个询问:
    • 首先,在树状数组上二分,求出满足 \(pre_0[i]\leqslant suf_1[i]\) 最大的 \(i\)。此时,设 \(pre_0[i]=v_0,pre_1[i]=v_1+val_1[i]\)
    • \(v_0=v_1=0\),那么显然 \(\texttt{Peace}\)
    • \(v_0>v_1\),那么 \(i\) 变大后答案最多为 \(v_1\),而此时答案为 \(v_0\),故最终位置即为 \(i\)
    • 否则,就把 \(i\) 再往后,使得答案为 \(v_1\),且 \(i\) 尽可能大。
#include<cstdio>
#include<algorithm>
using namespace std;
int Q,op[2100000],a[2100000],x[2100000],y[2100000];
int s0[2100000],s1[2100000];
int sum0,sum1,tree0[2100000],tree1[2100000];
int cnt,num[2100000];
char Getchar(){
	static char now[1<<20],*S,*T;
	if (T==S){
		T=(S=now)+fread(now,1,1<<20,stdin);
		if (T==S) return EOF;
	}
	return *S++;
}
int read(){
	int x=0,f=1;
	char ch=Getchar();
	while (ch<'0'||ch>'9'){
		if (ch=='-') f=-1;
		ch=Getchar();
	}
	while (ch<='9'&&ch>='0') x=x*10+ch-'0',ch=Getchar();
	return x*f;
}
void add(int *tree,int x,int v){
	for (;x<=cnt;x+=x&-x) tree[x]+=v;
}
void solve(){
	int p=0,v,v0=0,v1=sum1,new_p;
	for (int i=20;i>=0;i--){
		new_p=p|(1<<i);
		if (new_p<=cnt&&v0+tree0[new_p]<=v1-tree1[new_p]+s1[new_p]){
			p=new_p;
			v0+=tree0[p]; v1-=tree1[p];
		}
	}
	if (!v0&&!v1) puts("Peace");
	else
		if (v0>v1) printf("%d %d\n",num[p],(v0<<1));
   		else{
   			p=0; v=sum1-v1;
			for (int i=20;i>=0;i--){
				new_p=p|(1<<i);
				if (new_p<=cnt&&v>=tree1[new_p]){
					p=new_p;
					v-=tree1[p];
				}
			}
			printf("%d %d\n",num[p+1],(v1<<1));
		}
}
int main(){
	Q=read(); int t;
	for (int i=1;i<=Q;i++){
		op[i]=read(); t=read();
		if (op[i]==1){
			a[i]=t;
			x[i]=read(); y[i]=read();
			num[++cnt]=x[i];
		} else{
			a[i]=a[t];
			x[i]=x[t]; y[i]=-y[t];
		}
	}
	sort(num+1,num+cnt+1);
	cnt=unique(num+1,num+cnt+1)-num-1;
	for (int i=1;i<=Q;i++) x[i]=lower_bound(num+1,num+cnt+1,x[i])-num;
	for (int i=1;i<=Q;i++){
		if (!a[i]) add(tree0,x[i],y[i]),s0[x[i]]+=y[i],sum0+=y[i];
		else add(tree1,x[i],y[i]),s1[x[i]]+=y[i],sum1+=y[i];
		solve();
	}
	return 0;
}

Day1T2

  • 把普通多项式多项式转化成下降幂多项式的形式,\(f(k)=\sum_{i=0}^mb_ix^\underline i\)
  • 怎么转呢?利用 \(\displaystyle x^n=\sum_{k=1}^nS(n,k)x^\underline k\) 。其中第二类斯特林数 \(S(n,k)=S(n−1,k−1)+S(n−1,k)∗k\)

\[\sum_{k=0}^nf(k)\times x^k \times \binom{n}{k}=\sum_{k=0}^n\sum_{i=0}^{\min(m,k)}b_ik^\underline ix^k\binom{n}{k}\\=\sum_{i=0}^mb_in^\underline i\sum_{k=i}^{n}x^k\binom{n-i}{k-i}\\=\sum_{i=0}^mb_in^\underline ix^{i}(x+1)^{n-i} \]

#include<cstdio>
using namespace std;
typedef long long ll;
ll n,x,mod,m,ans;
ll a[1100],s[1100][1100];
ll qpow(ll x,ll a){
    ll res=1;
    while (a){
        if (a&1) res=res*x%mod;
        x=x*x%mod; a>>=1;
    }
    return res;
}
int main(){
    scanf("%lld%lld%lld%lld",&n,&x,&mod,&m);
    for (ll i=0;i<=m;i++) scanf("%lld",&a[i]);
    s[0][0]=1;
    for (ll i=1;i<=m;i++)
        for (ll j=1;j<=i;j++)
			s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*j)%mod;
    for (ll j=0;j<=m;j++){
        ll tmp=1,xp=1;
        for (ll i=0;i<=j;i++){
            ans=(ans+1ll*a[j]*s[j][i]%mod*tmp%mod*xp%mod*qpow(x+1,n-i))%mod;
            tmp=1ll*tmp*(n-i)%mod; xp=1ll*xp*x%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

Day1T3

30pts

  • 强基交换定理:对于任意拟阵\(M\),若\(M\)有两个不相同的基\(A,B\),那么\(\forall x\in A\setminus B,\exists y\in B\setminus A,s.t.A-\{x\}+\{y\},B-\{y\}+\{x\}\)都是\(M\)的基。
  • 我们只需考虑在 \(A/B\) 中删去一个元素,加入新的元素的基和它们的大小关系,只需枚举 \(A/B\) 中元素的价格,剩下的直接计算即可。

+10pts

  • 原来的所有的 \(\leqslant,\geqslant\) 的关系都变成 \(=\) 的关系。
  • 而点集 \(U\)\(L_2\) 均值为其加权平均数 \(\displaystyle \frac{\sum_{v_i\in U}w_iy_i}{\sum_{v_i\in U}w_i}\)(导数易证,定义见高睿泉《浅谈保序回归问题》IOI2018国家集训队论文集)。并查集合并相等关系。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll INF=1ll<<60;
int n,m,a[1100],b[1100],fa[1100];
int v[1100],x[1100],minv,maxv;
bool trans[1100][1100],vis[1100];
ull c[1100],num[64]; ll ans;
int cnt; int pos[1100];
vector<int> vec[1100];
int findset(int x){
	if (x!=fa[x]) fa[x]=findset(fa[x]);
	return fa[x];
}
void Union(int x,int y){
	x=findset(x); y=findset(y);
	if (x!=y) fa[y]=x;
}
void ins(ull x){
	for (int i=63;i>=0;i--)
		if ((x>>i)&1){
			if (!num[i]){ cnt++; num[i]=x; break;}
			x^=num[i];
		}
}
void work(){
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			cnt=0; memset(num,0,sizeof(num));
			int tmp=a[j];
			a[j]=i;
			for (int k=1;k<=m;k++) ins(c[a[k]]);
			a[j]=tmp;
			if (cnt==m) trans[a[j]][i]=1;//x[a[j]]<x[i]
		}
}
void work2(){
	for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			cnt=0; memset(num,0,sizeof(num));
			int tmp=b[j];
			b[j]=i;
			for (int k=1;k<=m;k++) ins(c[b[k]]);
			b[j]=tmp;
			if (cnt==m) trans[i][b[j]]=1;//x[i]<x[b[j]]
		}
}
void dfs(int u){
	if (u>cnt){
		ll sum=0;
		for (int i=1;i<=n;i++)
			if (vis[i]) sum+=1ll*(x[i]-v[i])*(x[i]-v[i]);
			else{
				int L=minv,R=maxv;
				for (int j=1;j<=cnt;j++){
                    if (trans[pos[j]][i]) L=max(L,x[pos[j]]);
                    if (trans[i][pos[j]]) R=min(R,x[pos[j]]);
                }
                if (L>R){ sum=INF; break;}
				if (v[i]<L) sum+=1ll*(L-v[i])*(L-v[i]);
                if (v[i]>R) sum+=1ll*(v[i]-R)*(v[i]-R);
            }
        ans=min(ans,sum); return;
	}
	int L=minv,R=maxv;
	for (int i=1;i<u;i++){
		if (trans[pos[i]][pos[u]]) L=max(L,x[pos[i]]);
		if (trans[pos[u]][pos[i]]) R=min(R,x[pos[i]]);
	}
	for (int i=L;i<=R;i++){ x[pos[u]]=i; dfs(u+1);}
}
int main(){
//	freopen("shop.in","r",stdin);
//	freopen("shop.out","w",stdout);
	scanf("%d%d",&n,&m);
	minv=1e6; maxv=0;
	for (int i=1;i<=n;i++) scanf("%llu",&c[i]);
	for (int i=1;i<=n;i++){
		scanf("%d",&v[i]);
		minv=min(minv,v[i]); maxv=max(maxv,v[i]);
	}
	for (int i=1;i<=m;i++){
		scanf("%d",&a[i]);
		vis[a[i]]=true;
	}
	for (int i=1;i<=m;i++){
		scanf("%d",&b[i]);
		vis[b[i]]=true;
	}
	work(); work2();
	cnt=0;
	for (int i=1;i<=n;i++)
		if (vis[i]) pos[++cnt]=i;
	if (cnt==m){
		for (int i=1;i<=n;i++) fa[i]=i;
		for (int i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
				if (trans[i][j]) Union(i,j);
		for (int i=1;i<=n;i++) vec[findset(i)].push_back(i);
		ll sum,sum1,sum2,now; int tot;
		for (int i=1;i<=n;i++){
			sum=0; tot=0;
			for (int j=0;j<(int)vec[i].size();j++) sum+=v[vec[i][j]],tot++;
			if (tot){
				now=(ll)(1.0*sum/tot);
				sum1=0; for (int j=0;j<(int)vec[i].size();j++) sum1+=1ll*(now-v[vec[i][j]])*(now-v[vec[i][j]]);
				now++;
				sum2=0; for (int j=0;j<(int)vec[i].size();j++) sum2+=1ll*(now-v[vec[i][j]])*(now-v[vec[i][j]]);
				ans+=min(sum1,sum2);
			}
		}
	} else{
		ans=INF; dfs(1);
	}
	printf("%lld\n",ans);
	return 0;
}

100pts

  • 这个做法要用到 \(30pts\) 建的图。(建图方法要优化一下)
  • 我们把图建出来之后,原题就变成了一个保序回归问题。
  • 由论文的知识,若要求每个物品的价格为 \(k/k+1\),这个问题的解。一定存在原问题的一
    组最优解(每个物品的价格没有限制的最小回归代价)且可以通过向 \([k,k+1]\) 取整得到这个问题的最优解。
  • 那么,我们求出每个物品价格为 \(k/k+1\) 时的答案,就能知道必有一组最优解,现在取 \(k\) 的物品最终价格都 \(\leq k\),取 \(k+1\) 的物品最终价格都 \(\geq k+1\)
  • 将两边分开递归计算答案,它们之间显然是相互不影响的。
  • \(k=mid=\frac{l+r}2\) 即可,用整体二分的思想,分成两部分,递归即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef unsigned long long ull;
const ull INF=1ll<<60;
const int infdep=0x3f3f3f3f;
int n,m,a[1100],b[1100],fa[1100];
int v[1100],x[1100],minv,maxv;
bool trans[1100][1100],vis[1100];
ull c[1100],num[64]; ull ans[1100];
int cnt; int pos[1100];
vector<int> vec[1100],edge[1100];
namespace dinic{
	int s,t,dep[1100]; ull F[310000];
	int vis[1100];//是否到达过该点
	int edgenum=1,V[310000],Next[310000],Head[1100];
	void addedge(int u,int v,ull f){
		V[++edgenum]=v; F[edgenum]=f;
		Next[edgenum]=Head[u];
		Head[u]=edgenum;
	}
	void link(int u,int v,ull f){
		addedge(u,v,f);
		addedge(v,u,0);
	}
	bool bfs(){
		for (int i=1;i<=t;i++) vis[i]=false,dep[i]=infdep;
		dep[s]=0;
		vis[s]=true;
		queue<int> que;
		que.push(s);
		while (!que.empty()){
			int u=que.front(); que.pop();
			vis[u]=false;
			for (int e=Head[u];e;e=Next[e]){
				int d=V[e];
				if (dep[d]>dep[u]+1&&F[e]){
					dep[d]=dep[u]+1;
					if (!vis[d]){
						que.push(d);
						vis[d]=true;
					}
				}
			}
		}
		return dep[t]<infdep;
	}
	ull dfs(int u,ull flow){
		if (u==t){ 
			vis[t]=true; //maxflow+=flow;
			return flow;
		}
		ull used=0;
		vis[u]=true;
		for (int e=Head[u];e;e=Next[e]){
			int d=V[e];
			if ((!vis[d]||d==t)&&F[e]&&dep[d]==dep[u]+1){
				int minflow=dfs(d,min(flow-used,F[e]));
				if (minflow!=0) F[e]-=minflow,F[e^1]+=minflow,used+=minflow;
				if (used==flow) break;
			}
		}
		return used;
	}
	void maxflow(){
		while (bfs()){
			vis[t]=1;
			while (vis[t]){
				for (int i=1;i<=t;i++) vis[i]=false;
				dfs(s,INF); 
			}
		}
	}
	void init(int m){
		s=m+1; t=m+2;
		edgenum=1;
		for (int i=1;i<=t;i++) Head[i]=0;
	}
}
inline ull sqr(int x){
	if (x<0) x=-x;
	return (ull)x*x;
}
void ins(ull x){
	for (int i=63;i>=0;i--)
		if ((x>>i)&1){
			if (!num[i]){ cnt++; num[i]=x; break;}
			x^=num[i];
		}
}
bool couldins(ull x){
	for (int i=63;i>=0;i--)
		if ((x>>i)&1){
			if (!num[i]) return 1;
			x^=num[i];
		}
	return 0;
}
void work(){
	/*for (int i=1;i<=n;i++)
		for (int j=1;j<=m;j++){
			if (a[j]==i) continue;
			cnt=0; memset(num,0,sizeof(num));
			int tmp=a[j];
			a[j]=i;
			for (int k=1;k<=m;k++) ins(c[a[k]]);
			a[j]=tmp;
			if (cnt==m) edge[i].push_back(a[j]); //x[a[j]]<x[i]
		}*/
	for (int j=1;j<=m;j++){
		cnt=0; memset(num,0,sizeof(num));
		for (int k=1;k<=m;k++)
			if (k!=j) ins(c[a[k]]);
		for (int i=1;i<=n;i++){
			if (a[j]==i) continue;
			if (cnt+couldins(c[i])==m) edge[i].push_back(a[j]); //x[a[j]]<x[i]
		}
	}	
}
void work2(){
	for (int j=1;j<=m;j++){
		cnt=0; memset(num,0,sizeof(num));
		for (int k=1;k<=m;k++)
			if (k!=j) ins(c[b[k]]);
		for (int i=1;i<=n;i++){
			if (b[j]==i) continue;
			if (cnt+couldins(c[i])==m) edge[b[j]].push_back(i); //x[i]<x[b[j]]
		}
	}
}
int p[1100],q[1100],id[1100];
void solve(int l,int r,int L,int R){ //x[a]<=x[b],\sum (x[i]-v[i])^2
	if (l>r) return;
	if (L==R){
		for (int i=l;i<=r;i++) ans[p[i]]=L;
		return;
	}
	int mid=(L+R)>>1;
	dinic::init(r-l+1);
	for (int i=l;i<=r;i++) id[p[i]]=i-l+1;
	int u;
	for (int i=l;i<=r;i++){
		u=p[i];
		if (v[u]>mid) dinic::link(i-l+1,dinic::t,sqr(v[u]-mid)-sqr(v[u]-mid-1));
		else dinic::link(dinic::s,i-l+1,sqr(mid+1-v[u])-sqr(mid-v[u]));
		for (int j:edge[u])
			if (id[j]) dinic::link(i-l+1,id[j],INF);
	}
	for (int i=l;i<=r;i++) id[p[i]]=0;
	dinic::maxflow();
	int x=l,y=r;
	for (int i=l;i<=r;i++)
		if (dinic::dep[i-l+1]!=infdep) q[x++]=p[i];
		else q[y--]=p[i];
	for (int i=l;i<=r;i++) p[i]=q[i];
	solve(l,x-1,L,mid); solve(y+1,r,mid+1,R);
}
int main(){
//	freopen("shop.in","r",stdin);
//	freopen("shop.out","w",stdout);
	scanf("%d%d",&n,&m);
	minv=1e6; maxv=0;
	for (int i=1;i<=n;i++) scanf("%llu",&c[i]);
	for (int i=1;i<=n;i++){
		scanf("%d",&v[i]);
		minv=min(minv,v[i]); maxv=max(maxv,v[i]);
	}
	for (int i=1;i<=m;i++){
		scanf("%d",&a[i]);
		vis[a[i]]=true;
	}
	for (int i=1;i<=m;i++){
		scanf("%d",&b[i]);
		vis[b[i]]=true;
	}
	work(); work2();
	for (int i=1;i<=n;i++) p[i]=i;
	solve(1,n,minv,maxv);
	ull sum=0;
	for (int i=1;i<=n;i++) sum+=sqr(v[i]-ans[i]);
	printf("%llu\n",sum);
	return 0;
}

Day2T1

  • 状压dp即可,将答案按系数分开计算。
  • 为了节省空间,求出入边的数量需要由一种奇怪的方法求,详见代码。
#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
int n,m,k,SL[110],SR[110],f[8500000];
int cnt[110][110],Log2[8500000],tot[8500000];
int top,st[8500000],L[110][110],R[110][110];
int main(){
	scanf("%d%d%d",&n,&m,&k);
	int pre,now;
	for (int i=1;i<=n;i++,pre=now){
		scanf("%d",&now); now--;
		if (i>1&&now!=pre){ cnt[pre][now]++; SL[pre]++; SR[now]++;}
	}
	int upperlim=(1<<m)-1; f[0]=0;
	for (int i=1;i<=upperlim;i++) f[i]=INF;
	for (int i=0;i<m;i++) Log2[1<<i]=i;
	int x;
	for (int s=0;s<=upperlim;s++){
		if (s){
			while (top&&(st[top]&s)!=st[top]) top--;
			x=Log2[s&-s]; st[++top]=s;
			for (int i=0;i<m;i++){
				L[top][i]=L[top-1][i]+cnt[i][x];
				R[top][i]=R[top-1][i]+cnt[x][i];
			}
		}
        //L,R算出的为i到s的边数与s到i的边数
		tot[s]=tot[s>>1]+(s&1); x=tot[s]+1;
		for (int i=0;i<m;i++)
			if (!(s&(1<<i)))
				f[s|(1<<i)]=min(f[s|(1<<i)],f[s]+x*(L[top][i]*k+R[top][i]-(SL[i]-L[top][i])+(SR[i]-R[top][i])*k));
	}
	printf("%d\n",f[upperlim]);
	return 0;
}

Day2T2

  • 只需要实现一个数据结构维护一个无序数集,支持全部+1、插入一个数、合并、查询异或和。把二进制位反过来建\(\texttt{01-Trie}\)即可。
  • \(\texttt{01-Trie}\) 合并的复杂度等同于线段树合并的复杂度。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,val[530000],ch[21000000][2],tot[21000000],rt[530000],cnt;
int edgenum=1,vet[530000],Next[530000],Head[530000];
long long ans;
void addedge(int u,int v){
	vet[++edgenum]=v;
	Next[edgenum]=Head[u];
	Head[u]=edgenum;
}
void insert(int &x,int v,int d){
	if (!x) x=++cnt;
	tot[x]^=1;
	if (d>20) return;
	insert(ch[x][(v>>d)&1],v,d+1);
}
int merge(int x,int y){
	if (!x||!y) return x|y;
	tot[x]^=tot[y];
	ch[x][0]=merge(ch[x][0],ch[y][0]);
	ch[x][1]=merge(ch[x][1],ch[y][1]);
	return x;
}
void dfs(int u){
	insert(rt[u],val[u],0);
	int v;
	for (int e=Head[u];e;e=Next[e]){
		v=vet[e]; dfs(v);
		int x=rt[v];
		for (int i=0;i<=20;i++){
			val[u]^=tot[x]<<i;
			swap(ch[x][0],ch[x][1]);
			x=ch[x][0];
		}
		rt[u]=merge(rt[u],rt[v]);
		val[u]^=val[v];
	}
	ans+=val[u];
}
int main(){
	scanf("%d",&n); int f;
	for (int i=1;i<=n;i++) scanf("%d",&val[i]);
	for (int i=2;i<=n;i++){
		scanf("%d",&f);
		addedge(f,i);
	}
	dfs(1);
	printf("%lld\n",ans);
	return 0;
}

Day2T3

  • 求所有生成树边权和:令每条边边权为 \(1+w_ix\) ,然后在模\(x^2\)下做普通矩阵树即可,时间复杂度\(O(n^3)\)
  • 莫比乌斯反演,可以发现答案为\(\sum_d\varphi(d)\times [\mbox{所有边都是}d\mbox{的倍数的生成树的边权和}]\),看上去需要做\(\max\{w_i\}\)次求行列式,但考虑到参与构成此部分的图中边的总数不超过 \(144m\),而一张少于\(n-1\)条边的图中一定不包含生成树,故求行列式的次数其实只有\(O(\frac{144m}{n-1})\)次,总复杂度可以估计为\(O(144n^4)\)
#include<cstdio>
#include<utility>
#include<cstring>
using namespace std;
const int Mod=998244353;
int n,m,ans,u[1100],v[1100],w[1100]; int tot[210000];
int cnt,phi[210000],p[210000]; bool vis[210000];
struct node{
	int a,b;
	node(int x=0,int y=0){ a=x; b=y;}
	node operator+(const node &x) const{ return node((a+x.a)%Mod,(b+x.b)%Mod);}
	node operator-(const node &x) const{ return node((a-x.a+Mod)%Mod,(b-x.b+Mod)%Mod);}
	node operator*(const node &x) const{ return node(1ll*a*x.a%Mod,(1ll*a*x.b+1ll*b*x.a)%Mod);}
} A[32][32];
int qpow(int x,int a){
    int res=1;
    while (a){
        if (a&1) res=1ll*res*x%Mod;
        x=1ll*x*x%Mod; a>>=1;
    }
    return res;
}
node getinv(const node x){
	int inv=qpow(x.a,Mod-2);
	return node(inv,1ll*(Mod-x.b)%Mod*inv%Mod*inv%Mod);
}
void init(int n){
	phi[1]=1; vis[1]=true;
	for (int i=2;i<=n;i++){
		if (!vis[i]) p[++cnt]=i,phi[i]=i-1;
		for (int j=1;i*p[j]<=n;j++){
			vis[i*p[j]]=true;
			if (i%p[j]==0){
				phi[i*p[j]]=phi[i]*p[j];
				break;
			}
			phi[i*p[j]]=phi[i]*(p[j]-1);
		}
	}
}
int gauss(int n){
	node res=node(1,0); bool rev=false;
	for (int i=1;i<=n;i++){
		if (!A[i][i].a){
			for (int j=i+1;j<=n;j++){
				if (A[j][i].a){
					rev=!rev;
					swap(A[i],A[j]);
					break;
				}
			}
		}
		node inv=getinv(A[i][i]);
		for (int j=i+1;j<=n;j++){
			node tmp=A[j][i]*inv;
			for (int k=i;k<=n;k++) A[j][k]=A[j][k]-tmp*A[i][k];
		}
		res=res*A[i][i];
	}
	if (rev) return (Mod-res.b)%Mod;
	else return res.b;
}
int getans(int val){
	memset(A,0,sizeof(A));
	for (int i=1;i<=m;i++){
		if (w[i]%val) continue;
		A[u[i]][u[i]]=A[u[i]][u[i]]+node(1,w[i]);
		A[v[i]][v[i]]=A[v[i]][v[i]]+node(1,w[i]);
		A[u[i]][v[i]]=A[u[i]][v[i]]-node(1,w[i]);
		A[v[i]][u[i]]=A[v[i]][u[i]]-node(1,w[i]);
	}
	return gauss(n-1);
}
void gettot(int x){
	for (int i=1;i*i<=x;i++){
		if (x%i==0){
			tot[i]++;
			if (i*i!=x) tot[x/i]++;
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++){
		scanf("%d%d%d",&u[i],&v[i],&w[i]);
		gettot(w[i]);
	}
	init(152501);
	for (int i=1;i<=152501;i++){
		if (tot[i]<n-1) continue;
		ans=(ans+1ll*phi[i]*getans(i))%Mod;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-07-16 21:58  hydd  阅读(157)  评论(1编辑  收藏  举报