题目大意
一幅有向图有 n 个结点,初始没有边。
有 q 个操作,四种类型:
- + o v k a1 ⋯ ak:加入边 (v,a1),⋯,(v,ak)
- + i v k a1 ⋯ ak:加入边 (a1,v),⋯,(ak,v)
- − o v k a1 ⋯ ak:删除边 (v,a1),⋯,(v,ak)
- − i v k a1 ⋯ ak:删除边 (a1,v),⋯,(ak,v)
加边之前会保证原来没有这条边,删边之前会保证原来有这条边。
每次操作后,可以得到一个连通性矩阵 a(ai,j=1 表示 i 能到 j),输出
(i,j=1∑nai,jAi−1Bj−1) mod 232
1≤n≤400, 1≤q≤800, 1≤A,B≤109
3s
解法1
每个点维护一个 bitset 表示它能到哪些点。
每次操作时,暴力重构 v 的 bitset,然后 bfs 一下把能到 v 的点找出来,按拓扑序更新它们的 bitset。维护 bitset 时顺便维护答案。
对于加边操作的更新,是 bitseti=bitseti∨bitsetv。这个的时间主要在于图的遍历,边表是 O(n2) 的,因此时间是 O(q(n2+n64n))=O(qn2)。
对于删边操作的更新,是 bitseti=⋁x∈out(i)bitsetx(out(i) 表示 i 的出点),由于之前是或操作不可撤销,因此这个要对于 i 枚举它的出点重新算,因此是 O(q(n2+n264n))=O(64qn3)。
因此总的时间是 O(64qn3),算出来 8 亿但是跑过去了。
解法2
上面的解法,加边很优秀,但是删边不太行,这是因为删边的时候由于维护的是“是否连通”,所以无法快速撤销一个出点的影响。
那什么可以撤销呢?方案数就可以撤销!
记 fi,j 表示 i 走到 j 的方案数,给它模个 109+7 啥的(不放心就多模几个)。
每次操作时,暴力重算 fv,⋅ 或者 f⋅,v,然后对于所有 (i,j),先 fi,j−=原来的fi,v+fv,j,再 +=新的fi,v+fv,j。
这样就是 O(qn2) 的了。
代码
// 解法1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| #include<bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;i++) using namespace std;
typedef unsigned int uint;
const int maxn=405;
int n,q,len,x[maxn]; uint A[maxn],B[maxn]; bool mp[maxn][maxn]; bitset<maxn> a[maxn];
void ReadChar(char &ch) { ch=getchar(); while (ch!='+' && ch!='-' && ch!='o' && ch!='i') ch=getchar(); }
bool bz[maxn]; void dfs1(int k,int v) { if (v!=k) a[v][k]=1; bz[k]=1; fo(i,1,n) if (mp[k][i] && !bz[i]) dfs1(i,v); }
int d[3*maxn],dg[maxn]; void topo(int v,char ty) { fo(i,1,n) bz[i]=0, dg[i]=0; int j=0; if (ty=='o') d[j=1]=v; else fo(i,1,len) d[++j]=x[i]; for(int i=1; i<=j; i++) { fo(go,1,n) if (mp[go][d[i]]) { dg[go]++; if (!bz[go]) bz[ d[++j]=go ]=1; } } }
uint ans,Ans[maxn]; void Redo(int v) { ans-=Ans[v]; Ans[v]=0; fo(i,1,n) if (a[v][i]) Ans[v]+=A[v-1]*B[i-1]; ans+=Ans[v]; }
int main() { freopen("reachability.in","r",stdin); freopen("reachability.out","w",stdout); scanf("%d %d %u %u",&n,&q,&A[1],&B[1]); A[0]=B[0]=1; fo(i,2,n) A[i]=A[i-1]*A[1], B[i]=B[i-1]*B[1]; while (q--) { char ch1,ch2; int v; ReadChar(ch1), ReadChar(ch2); scanf("%d %d",&v,&len); fo(i,1,len) { scanf("%d",&x[i]); if (ch2=='o') mp[v][x[i]]^=1; else mp[x[i]][v]^=1; } if (ch2=='o') { a[v].reset(); fo(i,1,n) bz[i]=0; dfs1(v,v); Redo(v); } topo(v,ch2); if (ch1=='+') { int j=0; if (ch2=='o') d[j=1]=v; else fo(i,1,len) d[++j]=x[i]; for(int i=1; i<=j; i++) { a[d[i]]|=a[v]; if (d[i]!=v) a[d[i]][v]=1; Redo(d[i]); fo(go,1,n) if (mp[go][d[i]]) { if (--dg[go]==0) d[++j]=go; } } } else { int j=0; if (ch2=='o') d[j=1]=v; else fo(i,1,len) d[++j]=x[i]; for(int i=1; i<=j; i++) { a[d[i]].reset(); fo(go,1,n) if (mp[d[i]][go]) a[d[i]]|=a[go], a[d[i]][go]=1; Redo(d[i]); fo(go,1,n) if (mp[go][d[i]]) { if (--dg[go]==0) d[++j]=go; } } } printf("%u\n",ans); } }
|