题解:P13542 [OOI 2022] Two avenues
难写的单
考虑一条边怎样产生贡献:
- 它是割边,被割开一定有贡献。
- 它是边双内部、边三内部的边,可以被绕开,不会有贡献。
- 它在边双内部,但不在边三内部,也就是位于某个环上,它对应一个二边割的一部分,只有和同一个环上的另一条边一起选才可能有贡献。
最优的两条大道只有两种可能:
- 选两条割边,显然取经过次数最多、次多的割边
- 选同一个环上的两条边,就是把环切两刀,使被切开的路径贡献最大。
第一类是好做的,边双缩点后得到树,可以树上差分求每条边经过次数。
对于第二类,在每个边双内部做边三连通分量缩点,每个边三缩成一个圆点,每个环缩成一个方点,形成一棵圆方树。一个方点
对于一个方点
想一下怎么算切开区间权值和的最大值。考虑扫描线,枚举第一刀,然后可以线段树维护第二刀落在所有位置的贡献,当一刀进入某个区间时,给这个区间减去贡献,它的补集加上贡献,离开区间同理。注意环上的区间要拆开处理。
我们还要把所有环上的区间弄出来,肯定不能直接遍历。还是树上差分,一个路径
最后时间复杂度
#include<bits/stdc++.h>
#define ll long long
using namespace std;
mt19937_64 gen(time(NULL));
uniform_int_distribution<uint64_t>dist(0,(1ULL<<63)-1);
ll Rnd(){return dist(gen);}
const int N=5e5+10,inf=1e9;
int Gr,g,n,m,K,cnt;
int ex[N],ey[N],a[N],b[N];
vector<pair<int,int> >v[N];
void link(int x,int y){
cnt++;
v[x].push_back({y,cnt});
v[y].push_back({x,cnt});
}
namespace part1{//割边切两刀
int dfu,top,cnt;
int stk[N],dfn[N],low[N],cut0[N*2],col[N];
void tarjan(int u,int fr){
dfn[u]=low[u]=++dfu,stk[++top]=u;
for(auto [to,id]:v[u]){
if(id==fr) continue;
if(!dfn[to]){
tarjan(to,id);
low[u]=min(low[u],low[to]);
if(low[to]>=dfn[to]) cut0[id]=1;
}
else low[u]=min(low[u],dfn[to]);
}
if(low[u]==dfn[u]){
cnt++;
while(stk[top]!=u) col[stk[top--]]=cnt;
col[stk[top--]]=cnt;
}
}
vector<int>tr[N];
void linkt(int x,int y){
tr[x].push_back(y);
tr[y].push_back(x);
}
struct qry{
int df,f[21][N],dfn[N],dep[N];
void dfs(int u,int fa){
f[0][dfn[u]=++df]=fa;
for(int t:tr[u])
if(t!=fa)
dep[t]=dep[u]+1,dfs(t,u);
}
int get(int x,int y){
return dfn[x]<dfn[y]?x:y;
}
void init(int n){
for(int i=1;i<=19;i++)
for(int j=1;j+(1<<i)-1<=n;j++)
f[i][j]=get(f[i-1][j],f[i-1][j+(1<<i-1)]);
}
int lca(int x,int y){
if(x==y) return x;
if((x=dfn[x])>(y=dfn[y])) swap(x,y);
x++;int d=log2(y-x+1);
return get(f[d][x],f[d][y-(1<<d)+1]);
}
}T;
int val[N];
void buildtree(){
for(int i=1;i<=m;i++)
if(col[ex[i]]!=col[ey[i]]) linkt(col[ex[i]],col[ey[i]]);
T.dfs(1,0),T.init(cnt);
}
void dfs(int u,int fa){
for(int to:tr[u]){
if(to==fa) continue;
dfs(to,u),val[u]+=val[to];
}
}
vector<int>solve(){
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i,0);
buildtree();
for(int i=1;i<=K;i++){
int x=col[a[i]],y=col[b[i]];
val[x]++,val[y]++,val[T.lca(x,y)]-=2;
}
dfs(1,0);
int mx1=0,mx2=0,a=0,b=0,c=0,d=0;
for(int i=2;i<=cnt;i++){
if(val[i]>mx1) mx2=mx1,c=a,d=b,mx1=val[i],a=i,b=T.f[0][T.dfn[i]];
else if(val[i]>mx2) mx2=val[i],c=i,d=T.f[0][T.dfn[i]];
}
for(int i=1;i<=m;i++){
int x=col[ex[i]],y=col[ey[i]];
if((x==a&&y==b)||(x==b&&y==a)){a=ex[i],b=ey[i];break;}
}
for(int i=1;i<=m;i++){
int x=col[ex[i]],y=col[ey[i]];
if((x==c&&y==d)||(x==d&&y==c)){c=ex[i],d=ey[i];break;}
}
return {mx1+mx2,a,b,c,d};
}
void Clear(){
for(int i=1;i<=cnt;i++) tr[i].clear();
for(int i=1;i<=n;i++) dfn[i]=low[i]=stk[i]=cut0[i]=col[i]=val[i]=0;
for(int i=1;i<=cnt;i++) T.dfn[i]=T.dep[i]=0;
for(int i=1;i<=m;i++) cut0[i]=0;
T.df=dfu=top=cnt=0;
}
};
namespace part2{//边双非边三切某个环
ll w[N*2];
int dfu,Cnt;
int dfn[N],ist[N*2],cut0[N*2],cut[N*2],fa[N],col[N];
map<ll,int>mp1,mp2;
void dfs3(int u,int t,int fr,int idx){//collect
col[u]=idx;
for(auto [to,id]:v[u]){
if(id==fr||cut0[id]||cut[id]||!ist[id]) continue;
if(to==t){ist[id]=0;continue;}
dfs3(to,t,id,idx);
}
}
void dfs2(int u,int fr){
int siz=v[u].size();
for(int i=0;i<siz;i++){
int to=v[u][i].first,id=v[u][i].second;
if(id==fr||cut0[id]||cut[id]||!ist[id]) continue;
dfs2(to,id);
}
if(mp2.count(w[u])){
int to=mp2[w[u]];
ist[fr]=0;
Cnt++,dfs3(u,to,0,Cnt);
ist[fr]=0;
link(fa[u],to);
ist[cnt]=1,fa[to]=fa[u];
mp2[w[u]]=to;
}
else mp2[w[u]]=u;
}
void dfs(int u,int fr){
dfn[u]=++dfu;
int siz=v[u].size();
for(int i=0;i<siz;i++){
int to=v[u][i].first,id=v[u][i].second;
if(id==fr||cut0[id]) continue;
if(dfn[to]){
if(dfn[to]>dfn[u]) continue;
ll val=Rnd();
w[u]^=val,w[to]^=val;
mp1[val]=1;
}
else{
ist[id]=1,fa[to]=u;
dfs(to,id),w[u]^=w[to];
}
}
if(fr&&mp1.count(w[u])) cut[fr]=1;
if(cut[fr]||!fr){
mp2.clear();
dfs2(u,0);
++Cnt,dfs3(u,0,0,Cnt);
}
}
void threecon(){
for(int i=1;i<=n;i++)
if(!dfn[i]) dfs(i,0);
}
namespace connected{//缩边三后圆方树
int n,cnt;//圆点1~n 方点n+1~n+cnt
vector<int>g[N];
void linkg(int x,int y){
g[x].push_back(y);
g[y].push_back(x);
}
void buildgraph(){
for(int i=1;i<=m;i++)
if(col[ex[i]]!=col[ey[i]])
linkg(col[ex[i]],col[ey[i]]);
}
vector<int>tr[N*2];
void link(int x,int y){
tr[x].push_back(y);
tr[y].push_back(x);
}
int top,dfu,stk[N],dfn[N],low[N],len[N];
void dfs(int u,int fa){
stk[++top]=u,low[u]=dfn[u]=++dfu;
for(int to:g[u]){
if(to==fa) continue;
if(!dfn[to]){
len[to]=len[u]+1;
dfs(to,u),low[u]=min(low[u],low[to]);
if(low[to]>=dfn[u]){
cnt++;
while(stk[top+1]!=to) link(n+cnt,stk[top--]);
link(n+cnt,u);
}
}
else low[u]=min(low[u],dfn[to]);
}
}
int dep[N*2],f[21][N*2],nid[N*2];
void dfs2(int u,int fa){
dep[u]=dep[fa]+1,f[0][u]=fa;
for(int i=0;i<=19;i++)
f[i+1][u]=f[i][f[i][u]];
auto it=find(tr[u].begin(),tr[u].end(),fa);
if(it!=tr[u].end()) tr[u].erase(it);
if(u>n){//方点处理环
sort(tr[u].begin(),tr[u].end(),[&](int x,int y){
return len[x]<len[y];
});
for(int i=0;i<tr[u].size();i++) nid[tr[u][i]]=i+1;
}
for(int to:tr[u]) dfs2(to,u);
}
int LCA(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[f[i][x]]>=dep[y]) x=f[i][x];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
return f[0][x];
}
int jump(int u,int jmp){
for(int i=20;i>=0;i--)
if(jmp>=(1<<i)) u=f[i][u],jmp-=(1<<i);
return u;
}
struct node{
int l,r,v;
node(int x,int y){l=min(x,y),r=max(x,y),v=1;}
node(int x,int y,int z){l=x,r=y,v=z;}
};
struct SGtree{//区间加区间max线段树
pair<int,int>tr[N*8];
int tag[N*8];
void adt(int x,int v){
tag[x]+=v,tr[x].first+=v;
}
void pushdown(int id){
if(tag[id]) adt(id*2,tag[id]),
adt(id*2+1,tag[id]),tag[id]=0;
}
void pushup(int id){
tr[id]=max(tr[id*2],tr[id*2+1]);
}
#define mid ((l+r)>>1)
void build(int id,int l,int r){
tag[id]=0;
if(l==r){tr[id]={0,l};return ;}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
pushup(id);
}
void modify(int id,int l,int r,int x,int y,int k){
if(x>y) return ;
if(x<=l&&r<=y){adt(id,k);return ;}
pushdown(id);
if(x<=mid) modify(id*2,l,mid,x,y,k);
if(mid+1<=y) modify(id*2+1,mid+1,r,x,y,k);
pushup(id);
}
pair<int,int>query(int id,int l,int r,int x,int y){
if(x>y) return {-inf,0};
if(x<=l&&r<=y) return tr[id];
pushdown(id);
if(y<=mid) return query(id*2,l,mid,x,y);
if(mid+1<=x) return query(id*2+1,mid+1,r,x,y);
return max(query(id*2,l,mid,x,y),query(id*2+1,mid+1,r,x,y));
}
#undef mid
}T;
int cro[N*2];
vector<node>jnt[N*2],vx[N*2];
void dfs3(int u){
for(int to:tr[u])
dfs3(to),cro[u]+=cro[to];
if(u>n){
for(int to:tr[u])
jnt[u].push_back({0,nid[to],cro[to]});
}
}
vector<int>work(int id){
int siz=tr[id].size()+1;
int res=0,a=0,b=0,c=0,d=0;
if(siz==2){
for(auto [l,r,v]:jnt[id]) res+=v;
a=c=f[0][id],b=d=tr[id][0];
return {res,a,b,c,d};
}
T.build(1,0,siz-1);
for(int i=0;i<siz;i++) vx[i].clear();
for(auto [l,r,v]:jnt[id]){
T.modify(1,0,siz-1,l,r-1,v);
vx[l].push_back({0,l-1,v});
vx[l].push_back({l,r-1,-v});
vx[l].push_back({r,siz-1,v});
vx[r].push_back({0,l-1,-v});
vx[r].push_back({l,r-1,v});
vx[r].push_back({r,siz-1,-v});
}
for(int i=0;i<siz-1;i++){
for(auto [l,r,v]:vx[i]) T.modify(1,0,siz-1,l,r,v);
auto rs=T.query(1,0,siz-1,i+1,siz-1);
if(rs.first>res){
res=rs.first;
if(i==0) a=f[0][id],b=tr[id][0];
else a=tr[id][i-1],b=tr[id][i];
if(rs.second==siz-1) c=tr[id][siz-2],d=f[0][id];
else c=tr[id][rs.second-1],d=tr[id][rs.second];
}
}
return {res,a,b,c,d};
}
vector<int>solve(){
buildgraph();
dfs(1,0),dfs2(1,0);
for(int i=1;i<=K;i++){
int x=col[a[i]],y=col[b[i]];
if(x==y) continue;
if(dep[x]>dep[y]) swap(x,y);
int lca=LCA(x,y);
if(lca==x) cro[x]--,cro[y]++;
else{
cro[x]++,cro[y]++;
x=jump(x,dep[x]-dep[lca]-1);
y=jump(y,dep[y]-dep[lca]-1);
cro[x]--,cro[y]--;
jnt[lca].push_back(node(nid[x],nid[y]));
}
}
dfs3(1);
int ans=0,a=0,b=0,c=0,d=0;
for(int id=n+1;id<=n+cnt;id++){
auto res=work(id);
if(res[0]>ans) ans=res[0],a=res[1],b=res[2],c=res[3],d=res[4];
}
vector<pair<int,int>>e;
for(int i=1;i<=m;i++){
int x=col[ex[i]],y=col[ey[i]];
if((x==a&&y==b)||(x==b&&y==a)) e.push_back({ex[i],ey[i]});
else if((x==c&&y==d)||(x==d&&y==c)) e.push_back({ex[i],ey[i]});
}
e.push_back({0,0});
e.push_back({0,0});
return {ans,e[0].first,e[0].second,e[1].first,e[1].second};
}
void Clear(){
for(int i=1;i<=n;i++)
stk[i]=dfn[i]=low[i]=len[i]=0,g[i].clear();
for(int i=1;i<=n+cnt;i++){
jnt[i].clear(),vx[i].clear();
cro[i]=0,tr[i].clear();
}
dfu=top=n=cnt=0;
}
};
vector<int>solve(){
threecon();
connected::n=Cnt;
return connected::solve();
}
void Clear(){
mp1.clear(),mp2.clear();
connected::Clear();
for(int i=1;i<=cnt;i++){
ist[i]=cut0[i]=cut[i]=0;
}
for(int i=1;i<=n;i++) dfn[i]=fa[i]=col[i]=w[i]=0;
dfu=Cnt=0;
}
};
void Clear(){
part1::Clear();
part2::Clear();
cnt=0;
for(int i=1;i<=n;i++) v[i].clear();
}
void print(vector<int>r){
cout<<r[0]<<"\n";
int a=r[1],b=r[2],c=r[3],d=r[4];
if(a>b) swap(a,b);
if(c>d) swap(c,d);
if((a==0||b==0)&&(c==0||d==0)){
cout<<ex[1]<<" "<<ey[1]<<"\n"<<ex[2]<<" "<<ey[2]<<"\n";
return ;
}
if((a==0)||(b==0)) swap(a,c),swap(b,d);
if((a==c&&b==d)||(c==0||d==0)){
for(int i=1;i<=m;i++)
if(ex[i]==a&&ey[i]==b);
else{c=ex[i],d=ey[i];break;}
}
cout<<a<<" "<<b<<"\n"<<c<<" "<<d<<"\n";
}
int main(){
cin>>Gr>>g;
while(Gr--){
cin>>n>>m;
for(int i=1;i<=m;i++){
scanf("%d%d",&ex[i],&ey[i]);
if(ex[i]>ey[i]) swap(ex[i],ey[i]);
link(ex[i],ey[i]);
}
cin>>K;
for(int i=1;i<=K;i++){
scanf("%d%d",&a[i],&b[i]);
}
vector<int>r1=part1::solve();
for(int i=1;i<=m;i++)
part2::cut0[i]=part1::cut0[i];
vector<int>r2=part2::solve();
if(r1[0]>r2[0]) print(r1);
else print(r2);
Clear();
}
return 0;
}
::::