uoj#189. 【集训队互测2016】火车司机出秦川
题意
给出一棵 N(≤3×105)N ( ≤ 3 × 10 5 ) 个点的仙人掌,每条边有一个价值。
Q(≤3×105)Q ( ≤ 3 × 10 5 ) 次操作,第 ii 次给出 Ki(ΣK≤3×105)K i ( Σ K ≤ 3 × 10 5 ) 个点对,询问这些点对间最短或最长路径的并的价值和,每次询问后接一次对一条边价值的修改。保证所有环都是奇环。
题解
【圆方树|uoj#189. 【集训队互测2016】火车司机出秦川 //圆方树】//小半年前刷专题的时候觉得挺麻烦的就没动…最近想锻炼一下代码能力所以翻出来写了
先把圆方树建出来,拿个bits维护一下每个点到根的最短路价值/最长路价值/树边价值,顺带着把环上价值的前缀和也维护了。
修改的时候树边影响其子树,环边会对环上某一段的子树的最长路和另一段的子树的最短路有影响。
询问时拎出来一个虚树算一算深度差,对于lca是个环的情况,在环上对涉及到的区间排序求并。
复杂度 O(nlogn)O ( n log ? n ) 。
代码
#include
#define N 600005
#define M 1200005
#define pb push_back
#define rg register int
#define Num(ite) ed[ite].size()/2
#define Mid(ite) ed[ite][Num(ite)]
using namespace std;
int n,m,q,to[M],hd[M],lk[N],len[M],cnt=1,cct,
dfn[N],low[N],st[N],ans,num[N],
dep[N],top[N],f[N],sz[N],son[N],t,w,
St[N],sum[N][2];
vectored[N],ve[N];
struct seg{int x,y;
};
struct bits
{
int a[N];
inline void ins(int x,int y)
{for(;
x<=n;
x+=x&-x)a[x]+=y;
}
inline int qry(int x)
{
rg y=0;
for(;
x;
x-=x&-x)y+=a[x];
return y;
}}Tr,Mx,Mn,Ci;
vectorsg[N];
char ch;
inline void rd(int &x)
{
for(;
(ch=getchar()-'0')<0||ch>9;
);
for(x=ch;
(ch=getchar()-'0')>=0&&ch<=9;
x=(x<<3)+(x<<1)+ch);
}
void dfs(int x)
{
dfn[x]=low[x]=++cnt;
for(rg s,i=lk[x];
i;
i=hd[i])
if(dfn[s=to[i]])low[x]=min(low[x],dfn[s]);
else if(st[t++]=i,dfs(s),low[s]>=dfn[x])
{
if(st[t-1]==i)ed[x].pb(s),t--;
else for(ed[x].pb(++n);
ed[n].pb(to[st[--t]]),to[st[t]]^s;
);
}
else low[x]=min(low[x],low[s]);
}
void dfss(int x)
{
dfn[x]=++cnt,dep[x]=dep[f[x]]+1,sz[x]=1;
if(x>m)
for(auto i:ed[x])
num[i]=++cct;
if(!num[x])num[x]=++cct;
for(auto i:ed[x])
f[i]=x,dfss(i),sz[x]+=sz[i];
}
void ssfd(int x)
{
!top[x]?top[x]=x:0;
for(auto i:ed[x])
sz[i]>sz[son[x]]?son[x]=i:0;
top[son[x]]=top[x];
for(auto i:ed[x])ssfd(i);
}
inline void add(int u,int v)
{to[++cnt]=v,hd[cnt]=lk[u],len[cnt]=w,lk[u]=cnt;
}
inline int check(int u,int v)
{
if(dep[u]+1==dep[v])return -1;
if(dep[u]==dep[v])
return num[u]dep[y];
x=f[top[x]])
if(f[top[x]]==y)return top[x];
return son[y];
}
inline void ad(int x,int y,int l,int r)
{
l=dfn[l],r=dfn[r];
if(x)ans+=Mn.qry(r)-Mn.qry(l);
if(y)ans+=Mx.qry(r)-Mx.qry(l);
}
bool cmpp(seg a,seg b)
{return num[a.y]>num[b.y];
}
void Dfs(int x)
{
rg le;
for(auto i:ve[x])
{
Dfs(i),sum[x][0]+=sum[i][0],sum[x][1]+=sum[i][1];
if(sum[i][0]||sum[i][1])
ans+=Tr.qry(dfn[i])-Tr.qry(dfn[x]);
if(x>m)le=nxt(i,x);
else le=x;
ad(sum[i][0],sum[i][1],le,i>m?f[i]:i);
}
if(x>m)
{
le=f[x];
for(auto i:ve[x])
{
if(sum[i][dfn[i]>=dfn[Mid(x)]])
sg[x].pb((seg){le,i});
if(sum[i][dfn[i]m&&f[v]^u)
w=nxt(v,u),ini(w),
ve[u].pb(w),ve[w].pb(v);
else ve[u].pb(v);
}
int main()
{
rd(n),rd(m),rd(q);
for(;
m--;
)
rd(u),rd(v),rd(w),add(u,v),add(v,u);
m=n,cnt=0;
dfs(1);
cnt=0,dfss(1),ssfd(1);
for(int i=2;
to[i];
i+=2)
mdf(i,len[i]);
for(al=1;
al<=q;
al++)
{
rd(k),ans=0;
if(k)
{
t=0;
for(int i=0;
idep[v];
)
{
if(lst)sad(St[cnt],lst);
lst=St[cnt--];
}
if(lst)sad(v,lst);
if(St[cnt]^v)St[++cnt]=v;
}
St[++cnt]=u;
}
for(;
cnt>1;
sad(St[cnt-1],St[cnt]),cnt--);
for(int i=0;
im)
{
u=nxt(u,w),v=nxt(v,w);
sum[u][o]--,sum[v][o]--;
if(num[u]>num[v])swap(u,v);
if((num[v]-num[u]>Num(w))^o)
sg[w].pb({v,w}),sg[w].pb({f[w],u});
else sg[w].pb({u,v});
}
else sum[w][o]-=2;
}
Dfs(St[1]);
}
printf("%d\n",ans);
rd(u),rd(v);
if(u)mdf(u<<1,v-len[u<<1]),len[u<<1]=len[u<<1|1]=v;
}
}
推荐阅读
- 并查集|[CF938G] Shortest Path Queries
- Different Integers
- 倍增|191026考试题解
- 树状数组|【HDU6635 Nonsense Time】树状数组维护最长上升子序列
- 51nod 1376 最长递增子序列的数量 树状数组