51nod 80 路径计数 容斥+建图

11 篇文章 0 订阅
题意:n点m条边的有向无环图,定义一条路径的价值为:该路径上所有边权的最大公约数.
n,w<=100,Q<=500,m<=5e4.Q次操作:修改一条边的权值,并询问价值为1的路径数量?


因为边权最大为100.可以拆成100个子图.第x个子图的路径值只能为x的倍数.
设g[i]为第i个子图的路径个数.容易容斥算出f[i],价值为i的路径总个数
因为为DAG,dp[u][x]以u为起点的价值为x倍数的路径个数.记忆化搜索求出g的值.O(n^3).


修改一条边的权值w,相当于更新所有d|w的子图 求一个子图的路径个数O(n^2) 

总的复杂度为O(Q*n^2*max(w的约数个数)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e2+5,M=5e4,mod=1e9+7;
ll n,m,id,w,Q,a[M],b[M],c[M],edg[N][N][N],g[N],f[N],dp[N];
ll dfs(int u,int x){
	if(dp[u]!=-1)	return dp[u];
	ll res=0;
	for(int j=1;j<=n;j++)
		if(edg[x][u][j])	res=(res+edg[x][u][j]*(1+dfs(j,x)))%mod;
	return dp[u]=res;	
}
ll calc(int x){
	memset(dp,-1,sizeof(dp));
	ll res=0;
	for(int i=1;i<=n;i++)	res=(res+dfs(i,x))%mod;
	return res;
}
void update(int u,int v,int w,int val){
	for(int j=1;j*j<=w;j++){
			if(w%j)	continue;
			edg[j][u][v]+=val;
			if(j*j!=w)	edg[w/j][u][v]+=val;
	}
}
void print(){
	for(int i=100;i>=1;i--){
		f[i]=g[i];
		for(int j=2*i;j<=100;j+=i)
			f[i]=(f[i]-f[j]+mod)%mod;
	}
	cout<<f[1]<<'\n';
} 
int main()
{
	ios::sync_with_stdio(false);cin.tie(0);
	memset(edg,0,sizeof(edg));
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>a[i]>>b[i]>>c[i];
		update(a[i],b[i],c[i],1);
	}
	for(int x=1;x<=100;x++)	g[x]=calc(x);//O(n^3)
	print();
	cin>>Q;
	while(Q--){
		cin>>id>>w;
		vector<int> vec;
		for(int j=1;j*j<=c[id];j++){
			if(c[id]%j)	continue;
			vec.push_back(j),edg[j][a[id]][b[id]]--;
			if(j*j!=c[id])	vec.push_back(c[id]/j),edg[c[id]/j][a[id]][b[id]]--;
		}
		c[id]=w;
		for(int j=1;j*j<=c[id];j++){
			if(c[id]%j)	continue;
			vec.push_back(j),edg[j][a[id]][b[id]]++;
			if(j*j!=c[id])	vec.push_back(c[id]/j),edg[c[id]/j][a[id]][b[id]]++;
		}
		for(int i=0;i<vec.size();i++)	g[vec[i]]=calc(vec[i]);
		//O(Q*(n^2*C) 
		print();
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值