[NOI2021] 庆典
\(\text{Problem}:\)[NOI2021] 庆典
\(\text{Solution}:\)
注意题目给出的性质保证了当 \(m=n-1\) 时,给出的图是一棵外向树。由此可以推出,将原图缩点后得到的图,一定由一棵外向树加上若干条从祖先指向后代的边构成。显然,不是外向树上的边没有用,故利用拓扑排序就可以去掉这些无用边。
考虑新加的边会产生哪些路径。不难发现,实际有用的结点(如两路径交点等)极少,故只需对新加边的端点及 \(s,t\) 建一棵虚树。然后从 \(s\) 开始遍历正图,从 \(t\) 开始遍历反图,最后求出交集的答案和即可。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=300010, M=20;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,Q,K;
int dfn[N],low[N],tim,sta[N],tp,book[N],bl[N],val[N],wal[N],cnt,deg[N],rt;
int f[N],d[N],son[N],siz[N],top[N],id[N],nowid,h[N],dis[N];
int head[N],maxE,ghead[N]; struct Edge { int nxt,to; }e[N<<1],g[N<<1];
struct NEdge { int u,v; }a[N<<1];
inline bool cp(NEdge x,NEdge y) { return x.u==y.u?x.v<y.v:x.u<y.u; }
inline void Add(int u,int v) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; }
struct Node { int v,id; };
vector<Node> g1[N],g2[N]; int tot;
int d1[N],d2[N],b1[N],b2[N];
int vc[N];
void Tarjan(int x)
{
dfn[x]=low[x]=++tim;
book[x]=1, sta[++tp]=x;
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(!dfn[v]) Tarjan(v), low[x]=min(low[x],low[v]);
else if(book[v]) low[x]=min(low[x],dfn[v]);
}
if(dfn[x]==low[x])
{
cnt++;
int y;
do
{
y=sta[tp--];
book[y]=0, bl[y]=cnt;
val[cnt]++;
}while(y!=x);
}
}
inline void Topo()
{
queue<int> Q;
for(ri int i=1;i<=n;i++) if(!deg[i]) Q.push(i);
while(!Q.empty())
{
int x=Q.front(); Q.pop();
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
deg[v]--;
if(!deg[v]) f[v]=x, Q.push(v);
}
}
}
void dfs1(int x)
{
dis[x]=dis[f[x]]+val[x];
siz[x]=1, d[x]=d[f[x]]+1;
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
dfs1(v);
siz[x]+=siz[v];
if(siz[v]>siz[son[x]]) son[x]=v;
}
}
void dfs2(int x,int topf)
{
top[x]=topf, id[x]=++nowid;
if(!son[x]) return;
dfs2(son[x],topf);
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==f[x]||v==son[x]) continue;
dfs2(v,v);
}
}
inline int LCA(int x,int y)
{
while(top[x]^top[y])
{
if(d[top[x]]<d[top[y]]) swap(x,y);
x=f[top[x]];
}
if(d[x]>d[y]) swap(x,y);
return x;
}
inline bool cp2(int x,int y) { return id[x]<id[y]; }
void DFS1(int x)
{
d1[x]=1;
for(auto i:g1[x])
{
b1[i.id]=1;
if(d1[i.v]) continue;
DFS1(i.v);
}
}
void DFS2(int x)
{
d2[x]=1;
for(auto i:g2[x])
{
b2[i.id]=1;
if(d2[i.v]) continue;
DFS2(i.v);
}
}
signed main()
{
n=read(), m=read(), Q=read(), K=read();
for(ri int i=1;i<=m;i++)
{
int u,v;
u=read(), v=read();
Add(u,v);
a[i]=(NEdge){u,v};
}
for(ri int i=1;i<=n;i++) if(!dfn[i]) Tarjan(i);
n=cnt, cnt=0;
for(ri int i=1;i<=m;i++)
{
if(bl[a[i].u]!=bl[a[i].v]) a[++cnt].u=bl[a[i].u], a[cnt].v=bl[a[i].v];
}
sort(a+1,a+1+cnt,cp), m=0;
for(ri int i=1;i<=cnt;i++)
{
if(i!=1 && a[i].u==a[i-1].u && a[i].v==a[i-1].v) continue;
a[++m]=a[i];
}
memset(head,0,sizeof(head)), maxE=0;
for(ri int i=1;i<=m;i++)
{
Add(a[i].u,a[i].v);
deg[a[i].v]++;
}
Topo();
memset(head,0,sizeof(head)), maxE=0;
for(ri int i=1;i<=n;i++)
{
if(!f[i]) rt=i;
else Add(f[i],i);
}
d[0]=-1, dfs1(rt), dfs2(rt,rt);
memset(book,0,sizeof(book));
for(;Q;Q--)
{
int len=0;
int s,t;
s=bl[read()], t=bl[read()];
h[1]=s, h[2]=t;
len=2;
for(ri int i=1;i<=K;i++)
{
a[i].u=bl[read()], a[i].v=bl[read()];
h[++len]=a[i].u, h[++len]=a[i].v;
}
sort(h+1,h+1+len,cp2); int sz=0;
for(ri int i=1;i<=len;i++) if(i==1||h[i]!=h[i-1]) h[++sz]=h[i];
len=sz, sz=0;
sta[tp=1]=rt; tot=0;
for(ri int i=1;i<=len;i++)
{
if(h[i]==rt) continue;
int pp=LCA(h[i],sta[tp]);
if(pp^sta[tp])
{
while(id[pp]<id[sta[tp-1]])
{
tot++; b1[tot]=b2[tot]=0;
wal[tot]=dis[sta[tp]]-dis[sta[tp-1]]-val[sta[tp]];
g1[sta[tp-1]].eb((Node){sta[tp],tot});
vc[++sz]=sta[tp-1], vc[++sz]=sta[tp];
tp--;
}
if(id[pp]>id[sta[tp-1]])
{
g1[pp].clear();
tot++; b1[tot]=b2[tot]=0;
wal[tot]=dis[sta[tp]]-dis[pp]-val[sta[tp]];
g1[pp].eb((Node){sta[tp],tot});
vc[++sz]=pp, vc[++sz]=sta[tp];
sta[tp]=pp;
}
else
{
tot++; b1[tot]=b2[tot]=0;
wal[tot]=dis[sta[tp]]-dis[pp]-val[sta[tp]];
g1[pp].eb((Node){sta[tp],tot});
vc[++sz]=pp, vc[++sz]=sta[tp];
tp--;
}
}
g1[h[i]].clear(), sta[++tp]=h[i];
}
for(ri int i=1;i<tp;i++)
{
tot++; b1[tot]=b2[tot]=0;
wal[tot]=dis[sta[i+1]]-dis[sta[i]]-val[sta[i+1]];
g1[sta[i]].eb((Node){sta[i+1],tot});
vc[++sz]=sta[i], vc[++sz]=sta[i+1];
}
for(ri int i=1;i<=K;i++)
{
tot++; b1[tot]=b2[tot]=0;
wal[tot]=0;
g1[a[i].u].eb((Node){a[i].v,tot});
vc[++sz]=a[i].u,vc[++sz]=a[i].v;
}
sort(vc+1,vc+sz+1); int nsz=0;
for(ri int i=1;i<=sz;i++) if(i==1 || vc[i]!=vc[i-1]) vc[++nsz]=vc[i];
sz=nsz;
for(ri int i=1;i<=sz;i++)
{
for(auto j:g1[vc[i]]) g2[j.v].eb((Node){vc[i],j.id});
}
DFS1(s); DFS2(t);
int ans=0;
for(ri int i=1;i<=sz;i++) if(d1[vc[i]]&&d2[vc[i]]) ans+=val[vc[i]];
for(ri int i=1;i<=tot;i++) if(b1[i]&&b2[i]) ans+=wal[i];
for(ri int i=1;i<=sz;i++)
{
g1[vc[i]].clear();
g2[vc[i]].clear();
d1[vc[i]]=d2[vc[i]]=0;
}
printf("%d\n",ans);
}
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。