P7737 [NOI2021] 庆典
lndjy
2021-08-16 14:56:08
这里提供一个虚树的做法,比起其他做法细节少了很多,并且可以扩展到 $\sum k\le 10^5$。
前面的部分和其他做法一样,先 tarjan 转 DAG,然后拓扑排序变成叶向树,变成树上问题。
随着 $k$ 的增长,分类讨论变得麻烦,考虑虚树。
将 $s,t$ 以及所有新边的两个端点拿出来,建立虚树,从祖先到后代连单向边(因为是叶向树)。同时点带点权为树上的点权,边带边权为两点之间不含两个端点的点权和。对于新加的边,根据定义在虚树上面多连一个边权为 0 的边。
现在变成和一开始一样的问题:给定一个的有向图,求 $s$ 到 $t$ 可能经过的权值和,只不过点边较少。
既然点边较少,那么原来的 35 分暴力做法就派上用场了:建原图跑出 $s$ 到达的点边,建反图跑出 $t$ 到达的点边,最后取一个交累加进答案。
代码较长,但是各部分互相独立比较好调,调试时只需要检查各种新图是不是对的就行了。
```cpp
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<unordered_map>
#include<map>
using namespace std;
inline int read()
{
char c=getchar();int x=0;bool f=0;
for(;!isdigit(c);c=getchar())f^=!(c^45);
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
if(f)x=-x;return x;
}
const int N=3e5+5;
int n,m,q,k,head[N],cnt,d[N];
struct Graph
{
int head[N],cnt;
struct edge
{
int to,nxt;
}e[N*2];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
}G1,G2;
struct graph
{
int head[20],cnt;
struct edge
{
int to,nxt,v;
}e[30*2];
void add(int u,int v,int w)
{
// cout<<u<<" "<<v<<" "<<w<<endl;
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
e[cnt].v=w;
}
void clear()
{
cnt=0;
memset(head,0,sizeof head);
}
}G3,G4;
struct edge
{
int to,nxt;
}e[N*2];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int dfn[N],low[N],v[N],col[N],colw[N],tot,s[N],top,Sum,st,ed,h[15],sum[N],lg[N],f[N][20],dep[N],Tot,L[N],ind[N];
#define sum Sum
void dfs(int now)//原图 tarjan
{
dfn[now]=low[now]=++tot;
s[++top]=now;
v[now]=1;
for(int i=head[now];i;i=e[i].nxt)
if(!dfn[e[i].to]) dfs(e[i].to),low[now]=min(low[now],low[e[i].to]);
else if(v[e[i].to]) low[now]=min(low[now],dfn[e[i].to]);
if(low[now]==dfn[now])
{
int x;
sum++;
do
{
x=s[top--];
col[x]=sum;
v[x]=0;
colw[sum]++;
}while(now!=x);
}
}
void topo()//G1DAG->G2 tree
{
queue<int> q;
for(int i=1;i<=sum;i++)
if(!d[i]) q.push(i);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=G1.head[now];i;i=G1.e[i].nxt)
{
d[G1.e[i].to]--;
if(d[G1.e[i].to]==0)
{
G2.add(now,G1.e[i].to);
ind[G1.e[i].to]++;
// cout<<now<<" "<<G1.e[i].to<<endl;
q.push(G1.e[i].to);
}
}
}
}
#undef sum
struct Hash
{
unordered_map<int,int> mp;
int cnt;
void clear()
{
mp.clear();
cnt=0;
}
int get(int x)
{
if(mp[x]==0) mp[x]=++cnt;
return mp[x];
}
}H;
void dfs1(int now,int fa)//G2 tree dfs :lca,dfn(L),权值前缀和(sum)
{
f[now][0]=fa;
L[now]=++Tot;
dep[now]=dep[fa]+1;
for(int i=1;i<=lg[dep[now]];i++)
f[now][i]=f[f[now][i-1]][i-1];
sum[now]=sum[fa]+colw[now];
for(int i=G2.head[now];i;i=G2.e[i].nxt)
{
if(G2.e[i].to!=fa)
dfs1(G2.e[i].to,now);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=f[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int i=lg[dep[x]];i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
bool cmp(int x,int y)
{
return L[x]<L[y];
}
int vis[30],vis2[30],vp[30],val[30];
int work()
{
memset(vis,0,sizeof vis);
memset(vp,0,sizeof vp);
queue<int> q;
// cout<<H.get(st)<<" "<<H.get(ed)<<endl;
q.push(H.get(st));
while(!q.empty())//G3 bfs
{
int now=q.front();q.pop();
vp[now]=1;
for(int i=G3.head[now];i;i=G3.e[i].nxt)
{
if(!vis[i])
{
vis[i]=1;
q.push(G3.e[i].to);
}
}
}
int ans=0;
memset(vis2,0,sizeof vis2);
q.push(H.get(ed));
while(!q.empty())//G4 bfs
{
int now=q.front();q.pop();
if(vp[now]) ans+=val[now],vp[now]=0;
for(int i=G4.head[now];i;i=G4.e[i].nxt)
{
if(!vis2[i])
{
vis2[i]=1;
if(vis[i]) ans+=G4.e[i].v,vis[i]=0;
q.push(G4.e[i].to);
}
}
}
memset(val,0,sizeof val);
return ans;
}
int main()
{
n=read();m=read();q=read();k=read();
for(int i=1;i<=n;i++)
lg[i]=lg[i/2]+1;
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
add(u,v);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i);
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=e[j].nxt)
if(col[i]!=col[e[j].to])
G1.add(col[i],col[e[j].to]),d[col[e[j].to]]++;
topo();
int root=0;
for(int i=1;i<=Sum;i++)
if(ind[i]==0) root=i;
dfs1(root,0);
for(int i=1;i<=q;i++)
{
G3.clear();G4.clear();H.clear();
st=read();ed=read();
st=col[st];ed=col[ed];
h[1]=st;h[2]=ed;
top=0;
for(int j=1;j<=k;j++)
{
int u=read(),v=read();
u=col[u];v=col[v];
if(u==v) continue;
G3.add(H.get(u),H.get(v),0);
G4.add(H.get(v),H.get(u),0);
h[j*2+1]=u;h[j*2+2]=v;
}
sort(h+1,h+(k+1)*2+1,cmp);
int qwq=unique(h+1,h+(k+1)*2+1)-h-1;
for(int j=1;j<=qwq;j++)
val[H.get(h[j])]=colw[h[j]];
s[++top]=h[1];
for(int j=2;j<=qwq;j++)
{
int lca=LCA(h[j],s[top]);
val[H.get(lca)]=colw[lca];
while(1)
{
if(dep[lca]>=dep[s[top-1]])
{
if(lca!=s[top])
{
G3.add(H.get(lca),H.get(s[top]),sum[f[s[top]][0]]-sum[lca]);
G4.add(H.get(s[top]),H.get(lca),sum[f[s[top]][0]]-sum[lca]);
if(lca!=s[top-1])
s[top]=lca;
else top--;
}
break;
}
else
{
G3.add(H.get(s[top-1]),H.get(s[top]),sum[f[s[top]][0]]-sum[s[top-1]]);
G4.add(H.get(s[top]),H.get(s[top-1]),sum[f[s[top]][0]]-sum[s[top-1]]);
top--;
}
}
s[++top]=h[j];
}
while(top-1)
{
// cout<<"qwq";
G3.add(H.get(s[top-1]),H.get(s[top]),sum[f[s[top]][0]]-sum[s[top-1]]);
G4.add(H.get(s[top]),H.get(s[top-1]),sum[f[s[top]][0]]-sum[s[top-1]]);
top--;
}
printf("%d\n",work());
}
return 0;
}
/*
7 6 1 1
1 2
1 3
2 4
3 5
2 6
6 7
6 4 7 1
*/
/*
5
*/
```