1 条题解
-
0
题面很长,但是仔细分析的话,这题不难。
早知道最后一节课就不去致毅馆外面唱K了。
题意
先给你一张有向图,每次进行操作:
1 u v:删除向边 ,保证它在原图中存在且未被删除。2 u:删除所有在原图中存在的有向边 。3 u v:恢复向边 ,保证它在原图中存在且被删除。4 u:恢复所有在原图中存在的有向边 。每次操作完之后,判断当前的图是否为一个内向基环树森林,也就是所有点的出度为 。
40 分做法
按照上述内容模拟即可,复杂度 。
60 分做法
注意到这样胡乱删除或修改,成为一片内向基环树森林的概率几乎为 ,所以暴力不能过的点全输出
NO。100 分做法
注意到题目要求我们完成的操作非常困难,而我们也无法使用数据解构维护每个点的出度。
回顾一下我们要判定什么:每个点是否只有恰好一条出边。
我们根本不在乎这个图长什么样,只要每个点只有一条出边即可。所以我们对于每个点 ,给所有原图中从 出发的边 附上一个相同的权值,这个权值随机赋一个数就行了。我们现在的目标就是判断:对于图中每条边 权值的和是否等于 ,如果相等,就鉴定为每个点出度为一。
然后考虑这个和如何修改。1,3 操作直接改。2,4 操作我们维护一个入边累计边权就行了。
代码
#include<bits/stdc++.h> #define R(x) x=read() #define int long long using namespace std; inline int read() { int x=0,y=1; char c=getchar(); while(c<'0'||c>'9') { if(c=='-') y=-1; c=getchar(); } while(c>='0'&&c<='9') { x=(x<<3)+(x<<1)+(c^'0'); c=getchar(); } return x*y; } mt19937 rd(time(0)); const int N=500005; int n,m,q; int a[N],nw,s; int ind[N],tot[N]; signed main() { R(n),R(m); for(int i=1;i<=n;++i){ a[i]=rd()%10000000000000; s+=a[i]; } while(m--) { int R(x),R(y); nw+=a[x]; ind[y]+=a[x]; tot[y]+=a[x]; } R(q); while(q--){ int R(opt),u,v; if(opt==1){ R(u),R(v); nw-=a[u]; ind[v]-=a[u]; }else if(opt==2){ R(u); nw-=ind[u]; ind[u]=0; }else if(opt==3){ R(u),R(v); nw+=a[u]; ind[v]+=a[u]; }else{ R(u); nw+=tot[u]-ind[u]; ind[u]=tot[u]; } puts(nw==s?"YES":"NO"); } return 0; }
- 1
信息
- ID
- 480
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 1
- 已通过
- 1
- 上传者