[51nod1610]路径计数

  路径上所有边权的最大公约数定义为一条路径的值。
  给定一个有向无环图。
  T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模)。
 Input
  第一行两个整数n和m,分别表示有向无环图上的点数和边数。(1<=n<=100,1<=m<=50,000)
  第2~m+1行每行三个数x,y,z,表示有一条从x到y权值为z的边。(1<=x,y<=n,1<=z<=100)
  第m+2行一个数T,表示修改操作次数(1<=T<=500)。
  接下来T行每行两个数x,y,表示修改第x条边(按照读入的顺序)的边权为y(1<=x<=m,1<=y<=100)。
 Output
  T+1行,修改前和每次修改操作后输出答案。

  朴素的想法...一开始先求出f[i][j]表示在DAG上从i点开始的路径的值为j的方案数,然后每次修改边权的时候,只要求g[i][j]表示从i点开始,经过修改的那条边的路径的值为j的方案数,然后更新f[i][j]就好了(修改就是一次删除一次添加)。

  为了跑得快一点...可以先求出拓扑序、预处理出每个点可以到达哪些点、修改的时候先计算一下有哪些路径值可能被影响...以避免不必要的计算和更新T_T

  复杂度O(100*n*m+T*因子个数*m)大概4亿左右...最后900+ms险过..

  正解是容斥、floyd....其实复杂度和正解也差不多?(常数感人

值为1的路径数量= sum{ 值为x的倍数的路径数量∗u[x] } ,1<=x<=100, u[x] 为莫比乌斯函数。
值为x的倍数的路径数量可以把所有边权为x的倍数的边取出来计算路径条数。
dp[x][i][j]表示只考虑所有边权为x的倍数的边时,点i到点j的路径数量。

初始化的时候,枚举x,然后跑一次floyd即可。
那么删除一条边权为k的倍数的、从x到y的边,可以用如下代码完成:

1 for(i=1;i<=n;++i)
2 {
3     for(j=1;j<=n;j++)
4     {
5         dp[k][i]][j]-=dp[k][i][x]*dp[k][y][j];
6     }
7 }

添加一条边类似,如此便可以做到修改边权的操作。
时间复杂度 O(100∗n^3+T∗因子个数∗n^2) 。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<queue>
  6 #include<cmath>
  7 #include<bitset>
  8 #define ll long long
  9 #define ui unsigned int
 10 #define ull unsigned long long
 11 #define d double
 12 const int maxn=105,mxm=50023,modd=1000000007;
 13 struct zs1{int too,pre,dis;}e[mxm];int tot,last[maxn],x[mxm],y[mxm],val[mxm];
 14 struct zs{int zt,id;}a[maxn];int cnt;
 15 int p[maxn],np,mn[maxn],bel[maxn],zt[maxn];
 16 int dl[maxn],pos[maxn],deg[maxn],too[66][maxn];
 17 int f[maxn][66],g[maxn][9];
 18 int i,j,k,n,m;
 19 char s[maxn];
 20
 21 int ra;char rx;
 22 inline int read(){
 23     rx=getchar(),ra=0;
 24     while(rx<‘0‘||rx>‘9‘)rx=getchar();
 25     while(rx>=‘0‘&&rx<=‘9‘)ra*=10,ra+=rx-48,rx=getchar();return ra;
 26 }
 27
 28
 29 inline void MOD(int &x){if(x>=modd)x-=modd;}
 30 inline void UPD(int &x){if(x<0)x+=modd;if(x>=modd)x-=modd;}
 31
 32 bool cmpa(zs a,zs b){return a.zt<b.zt;}
 33 inline void prerun(){
 34     a[1]=(zs){0,1};
 35     for(i=2;i<=100;i++){
 36         for(j=1;j<=np;j++)if(!(i%p[j]))break;
 37         if(j>np)p[++np]=i,mn[i]=np;else mn[i]=j;
 38         int tmp=i;
 39         while(tmp>1)a[i].zt|=1<<(mn[tmp]-1),tmp/=p[mn[tmp]];
 40         a[i].id=i;
 41     }
 42     cnt=0,a[0].zt=-233,
 43     std::sort(a+1,a+1+100,cmpa);
 44     for(i=1;i<=100;bel[a[i].id]=cnt,i++)
 45         if(a[i].zt!=a[i-1].zt)zt[++cnt]=a[i].zt;
 46
 47     for(i=0;i<=cnt;i++)for(j=1;j<=100;j++){
 48         int now=zt[bel[j]],k;
 49         if(i)now&=zt[i];
 50         for(k=1;zt[k]!=now;k++);
 51         too[i][j]=k;
 52     }
 53 }
 54
 55 inline void insert(int a,int b,int c){e[++tot].too=b,e[tot].dis=c,e[tot].pre=last[a],last[a]=tot;}
 56
 57 std::bitset<105> con[105];
 58 inline void topo(){
 59     int l=0,r=0,i,j,now,to;
 60     for(i=1;i<=n;i++){
 61         con[i][i]=1;
 62         if(!deg[i])dl[++r]=i;
 63     }
 64     for(i=1;i<=n;i++)f[i][0]=1;
 65     while(l<r)for(i=last[now=dl[++l]];i;i=e[i].pre){
 66         to=e[i].too,
 67         con[to]|=con[now];
 68         if(!--deg[to])dl[++r]=to;
 69         for(j=0;j<=cnt;j++)MOD(f[to][too[j][e[i].dis]]+=f[now][j]);
 70     }
 71     for(i=1;i<=n;i++)pos[dl[i]]=i;
 72 }
 73
 74 int mp[23],id[maxn];
 75 inline void getg(int edge,int fh){
 76     register int i,j,k;
 77     int e_id=bel[val[edge]],e_x=x[edge],e_y=y[edge],e_dis=val[edge];
 78     int num=0,now,to;
 79     for(i=1;i<=cnt;i++)if((zt[i]&zt[e_id])==zt[i])mp[++num]=i,id[i]=num;
 80
 81     memset(g[e_x]+1,0,num<<2);
 82     for(i=0;i<=cnt;i++)if(f[e_y][i])MOD(g[e_x][id[too[i][e_dis]]]+=f[e_y][i]);
 83     for(i=pos[e_x]+1;i<=n;i++){
 84         if(!con[now=dl[i]][e_y])continue;
 85         for(j=1;j<=num;j++)g[now][j]=0;
 86         for(j=last[now];j;j=e[j].pre)if(con[(to=e[j].too)][e_x])
 87             for(k=num;k;k--)
 88                 MOD(g[now][id[too[mp[k]][e[j].dis]]]+=g[to][k]);
 89     }
 90     for(i=pos[e_x];i<=n;i++)if(con[now=dl[i]][e_x])
 91         for(j=num;j;j--)if(g[now][j])UPD(f[now][mp[j]]+=fh*g[now][j]);
 92 }
 93
 94
 95 inline int getans(){
 96     int ans=0;for(int i=1;i<=n;i++)MOD(ans+=f[i][1]);
 97     return ans;
 98 }
 99 int main(){
100     prerun();
101     n=read(),m=read();
102     for(i=1;i<=m;i++)
103         x[i]=read(),y[i]=read(),val[i]=read(),
104         deg[x[i]]++,insert(y[i],x[i],val[i]);
105     topo(),printf("%d\n",getans());
106
107     int tmp=tot;tot=0,memset(last+1,0,n<<2);
108     for(i=1;i<=tmp;i++)insert(x[i],y[i],val[i]);
109
110     for(int q=read();q;q--){
111         int edge=read(),v=read();
112         getg(edge,-1),val[edge]=e[edge].dis=v,getg(edge,1),printf("%d\n",getans());
113     }
114 }

时间: 2024-10-08 11:13:03

[51nod1610]路径计数的相关文章

51 nod 1610 路径计数(Moblus+dp)

1610 路径计数 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 路径上所有边权的最大公约数定义为一条路径的值. 给定一个有向无环图.T次修改操作,每次修改一条边的边权,每次修改后输出有向无环图上路径的值为1的路径数量(对1,000,000,007取模). Input 第一行两个整数n和m,分别表示有向无环图上的点数和边数.(1<=n<=100,1<=m<=50,000) 第2~m+1行每行三个数x,y,z,表示有一条从x到y权值为z的边.(1

洛谷 P1176 路径计数2

P1176 路径计数2 题目描述 一个N×N的网格,你一开始在(1, 1),即左上角.每次只能移动到下方相邻的格子或者右方相邻的格子,问到达(N, N),即右下角有多少种方法. 但是这个问题太简单了,所以现在有M个格子上有障碍,即不能走到这M个格子上. 输入输出格式 输入格式: 输入文件第1行包含两个非负整数N,M,表示了网格的边长与障碍数. 接下来M行,每行两个不大于N的正整数x, y.表示坐标(x, y)上有障碍不能通过,且有1≤x, y≤n,且x, y至少有一个大于1,并请注意障碍坐标有可

【51nod】1776 路径计数

[51nod]1776 路径计数 我们先把前两种数给排好,排好之后会有\(a + b + 1\)个空隙可以填数,我们计算有\(k\)个空隙两端都是相同字母的方案数 可以用枚举把第二种数分成几段插进去来算,设这个方案数为\(f[k]\) 然后对于一种有\(k\)个空隙的方案数,枚举剩下的\(a + b + 1 - k\)个空隙填了\(h\)个 然后计算把\(C\)和\(D\)分成\(k + h\)段的方案数就好了,记为\(g[k + h]\) 那么如何计算\(g[i]\)呢 一段要么是偶数,\(C

HDU 6116 路径计数

HDU 6116 路径计数 普通生成函数常用于处理组合问题,指数生成函数常用于处理排列问题. 考虑 对于 $ a $ 个 $ A $ 分为很多堆,这么分的方案数是 $ C_{a-1}^{i-1} $ 然后对于每一堆我们看成一个数来放,并且所有堆都这样做,这样的话总的方案数量是 $ \frac{(i+j+k+l)!}{i!j!k!l!} $ 就算所有一堆看成的数的排列是不存在相邻相等的,至少都有 $ n-i-j-k-l $ 对相邻的相同的数. 然后就可以容斥了,枚举 $ i+j+k+l $ 直接计

BZOJ3697 采药人的路径 【点分治】

题目 采药人的药田是一个树状结构,每条路径上都种植着同种药材. 采药人以自己对药材独到的见解,对每种药材进行了分类.大致分为两类,一种是阴性的,一种是阳性的. 采药人每天都要进行采药活动.他选择的路径是很有讲究的,他认为阴阳平衡是很重要的,所以他走的一定是两种药材数目相等的路径.采药工作是很辛苦的,所以他希望他选出的路径中有一个可以作为休息站的节点(不包括起点和终点),满足起点到休息站和休息站到终点的路径也是阴阳平衡的.他想知道他一共可以选择多少种不同的路径. 输入格式 第1行包含一个整数N.

CH Round #30 摆花[矩阵乘法]

摆花 CH Round #30 - 清明欢乐赛 背景及描述 艺术馆门前将摆出许多花,一共有n个位置排成一排,每个位置可以摆花也可以不摆花.有些花如果摆在相邻的位置(隔着一个空的位置不算相邻),就不好看了.假定每种花数量无限,求摆花的方案数. 输入格式 输入有1+m行,第一行有两个用空格隔开的正整数n.m,m表示花的种类数.接下来的m行,每行有m个字符1或0,若第i行第j列为1,则表示第i种花和第j种花不能排在相邻的位置,输入保证对称.(提示:同一种花可能不能排在相邻位置). 输出格式 输出只有一

栈与队列问题1——出栈序列

问题描述:栈是常用的一种数据结构,有n个元素在栈顶端一侧等待进栈,栈顶端另一侧是出栈序列.你已经知道栈的操作有两种:push和pop,前者是将一个元素进栈,后者是将栈顶元素弹出.现在要使用这两种操作,由一个操作序列可以得到一系列的输出序列.请你编程求出对于给定的n,计算并输出由操作数序列1,2,…,n,经过一系列操作可能得到的输出序列总数. 分析:之前就有看过这种问题.就是火车进站问题,判断序列是否合法,当时是用STL栈做的.这个题只需统计次数,那么,方法就十分简便了,递归和动归都可以实现,当然

NOI前总结:点分治

点分治: 点分治的题目基本一样,都是路径计数. 其复杂度的保证是依靠 $O(n)$ 找重心的,每一次至少将问题规模减小为原先的$1/2$. 找重心我喜欢$BFS$防止爆栈. 1 int Root(int x){ 2 dfsn[0]=0; 3 q.push(x); fa[x]=0; 4 flag[x]=1; 5 while(!q.empty()){ 6 int x=q.front(); q.pop(); 7 dfsn[++dfsn[0]]=x; 8 for(int i=g[x];i;i=E[i].

最短路和次短路问题,dijkstra算法

1 /* 2  *题目大意: 3  *在一个有向图中,求从s到t两个点之间的最短路和比最短路长1的次短路的条数之和; 4  * 5  *算法思想: 6  *用A*求第K短路,目测会超时,直接在dijkstra算法上求次短路; 7  *将dist数组开成二维的,即dist[v][2],第二维分别用于记录最短路和次短路; 8  *再用一个cnt二维数组分别记录最短路和次短路的条数; 9  *每次更新路径的条数时,不能直接加1,,应该加上cnt[u][k],k为次短路径或者最短路径的标记; 10  *