由於我們不能每次都以 u 爲根重新求一遍,所以任選一個點爲根後,如果 v 是 u 的子節點,那麼可以直接利用各種方法(如線段樹合併)統計,否則分刪掉的子樹是否在 1 到 u 的路徑上進行處理
O(nlogn)
Code
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}template<classT>inlinevoidSwap(T &a, T &b){T t = a; a = b; b = t;}typedeflonglong ll;constint N =3e5+5, M = N <<1, L =1e7+5;int n, ecnt, nxt[M], adj[N], go[M], sze[N], rt[N], ToT, A[N], sum[N];
ll ans;struct node
{int lc, rc, sum;} T[L];voidchange(int x,int v){for(; x <= n; x += x &-x)
A[x]+= v;}intask(int x){int res =0;for(; x; x -= x &-x)
res += A[x];return res;}voidins(int l,int r,int pos,int v,int&p){if(!p) p =++ToT;
T[p].sum += v;if(l == r)return;int mid = l + r >>1;if(pos <= mid)ins(l, mid, pos, v, T[p].lc);elseins(mid +1, r, pos, v, T[p].rc);}intquery(int l,int r,int s,int e,int p){if(!p || e < l || s > r)return0;if(s <= l && r <= e)return T[p].sum;int mid = l + r >>1;returnquery(l, mid, s, e, T[p].lc)+query(mid +1, r, s, e, T[p].rc);}intmer(int x,int y){if(!x ||!y)return x ^ y;
T[x].sum += T[y].sum;
T[x].lc =mer(T[x].lc, T[y].lc);
T[x].rc =mer(T[x].rc, T[y].rc);return x;}voidadd_edge(int u,int v){
nxt[++ecnt]= adj[u]; adj[u]= ecnt; go[ecnt]= v;
nxt[++ecnt]= adj[v]; adj[v]= ecnt; go[ecnt]= u;}voiddfs(int u,int fu){
sze[u]=1;for(int e = adj[u], v; e; e = nxt[e])if((v = go[e])!= fu)dfs(v, u), sze[u]+= sze[v];}voidsolve(int u,int fu){int pm =0, pc =0;for(int e = adj[u], v; e; e = nxt[e]){if((v = go[e])== fu)continue;if(sze[v]> pm) pc = pm, pm = sze[v];elseif(sze[v]> pc) pc = sze[v];}if(fu){if(n - sze[u]> pm) pc = pm, pm = n - sze[u];elseif(n - sze[u]> pc) pc = n - sze[u];}for(int e = adj[u], v; e; e = nxt[e]){if((v = go[e])== fu)continue;change(sze[v],1);solve(v, u);change(sze[v],-1);int mx = sze[v]== pm ? pc : pm;int l = n - sze[v], r = mx -(l - mx);
l = sze[v]- l; r = sze[v]- r;if(l > r || r <1|| l > n){rt[u]=mer(rt[u], rt[v]);continue;}if(l <1) l =1;if(r > n) r = n;
ans +=1ll* u *query(1, n, l, r, rt[v]);
rt[u]=mer(rt[u], rt[v]);}if(u ==1)return;int mx = n - sze[u]== pm ? pc : pm;int l = sze[u], r = mx -(l - mx);
l = n - sze[u]- l; r = n - sze[u]- r;if(l > r || r <1|| l > n)returnins(1, n, sze[u],1, rt[u]);if(l <1) l =1;if(r > n) r = n;int cnt = sum[r]- sum[l -1]-query(1, n, l, r, rt[u]);
cnt -=ask(r)-ask(l -1);
l = n - l; r = n - r;Swap(l, r);if(l > r || r <1|| l > n)returnins(1, n, sze[u],1, rt[u]);if(l <1) l =1;if(r > n) r = n;
cnt +=ask(r)-ask(l -1); ans +=1ll* u * cnt;ins(1, n, sze[u],1, rt[u]);}voidwork(){int x, y;read(n);
ecnt = ToT =0; ans =0;memset(adj,0,sizeof(adj));memset(rt,0,sizeof(rt));memset(A,0,sizeof(A));memset(sum,0,sizeof(sum));for(int i =1; i < n; i++)read(x),read(y),add_edge(x, y);dfs(1,0);for(int i =2; i <= n; i++) sum[sze[i]]++;for(int i =1; i <= n; i++) sum[i]+= sum[i -1];solve(1,0);printf("%lld\n", ans);for(int i =1; i <= ToT; i++) T[i].lc = T[i].rc = T[i].sum =0;}intmain(){int T;read(T);while(T--)work();return0;}
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}typedeflonglong ll;constint N =4e7+5, M =1e5+5, rqy =1<<30, dmy =1e9;int n, ty, f[N], X, Y, Z, m, p[M], l[M], r[M], top, stk[N];
ll sum[N], tmp[4];struct gao
{int a[4];friendinline gao operator+(gao a, gao b){
gao res;
res.a[0]= res.a[1]= res.a[2]= res.a[3]=0;
res.a[0]+= a.a[0]+ b.a[0];if(res.a[0]>= dmy) res.a[1]++, res.a[0]-= dmy;
res.a[1]+= a.a[1]+ b.a[1];if(res.a[1]>= dmy) res.a[2]++, res.a[1]-= dmy;
res.a[2]+= a.a[2]+ b.a[2];if(res.a[2]>= dmy) res.a[3]++, res.a[2]-= dmy;return res.a[3]+= a.a[3]+ b.a[3], res;}} ans, tm;
gao sqr(gao x){
tmp[0]=1ll* x.a[0]* x.a[0]; tmp[1]=2ll* x.a[0]* x.a[1];
tmp[2]=1ll* x.a[1]* x.a[1]; tmp[3]=0;for(int i =0; i <3; i++)
tmp[i +1]+= tmp[i]/ dmy, tmp[i]%= dmy;
gao res; res.a[0]= tmp[0]; res.a[1]= tmp[1];return res.a[2]= tmp[2], res.a[3]= tmp[3], res;}intmain(){read(n);read(ty);if(ty){read(X);read(Y);read(Z);read(sum[1]);read(sum[2]);read(m);for(int i =3; i <= n; i++)
sum[i]=(sum[i -1]* X + sum[i -2]* Y + Z)% rqy;for(int i =1; i <= m; i++)read(p[i]),read(l[i]),read(r[i]);for(int j =1; j <= m; j++)for(int i = p[j -1]+1; i <= p[j]; i++)
sum[i]%= r[j]- l[j]+1, sum[i]+= l[j];}elsefor(int i =1; i <= n; i++)read(sum[i]);for(int i =1; i <= n; i++) sum[i]+= sum[i -1];
stk[top =1]=0;int p =1;for(int i =1; i <= n; i++){while(p < top && sum[stk[p +1]]*2- sum[f[stk[p +1]]]<= sum[i]) p++;
f[i]= stk[p];while(top && sum[stk[top]]*2- sum[f[stk[top]]]>= sum[i]*2- sum[f[i]]) top--;if(p > top) p = top; stk[++top]= i;}for(int i = n; i; i = f[i]){
ll num = sum[i]- sum[f[i]];
tm.a[0]= num % dmy; tm.a[1]= num / dmy;
ans = ans +sqr(tm);}bool is =0;for(int i =3; i >=0; i--){if(!ans.a[i]&&!is)continue;if(!is)printf("%d", ans.a[i]), is =1;elseprintf("%09d", ans.a[i]);}if(is)puts("");elseputs("0");return0;}
D1T3 tree
Solution
先按字典序從左到右貪心,設數 i 在點 u
現在要爲 u 選定一個編號最小的點 v (u=v),且需要滿足一些條件
設前 i−1 個數已經定好了位置,我們現在要判定的就是如果想要把數 i 移到點 v ,那麼是否存在一個操作次序
這時從 u 到 v 連一條路徑,可以得出:
(1)路徑上第一條邊比 u 出發的任意其他邊的操作次序都早
(2)路徑上最後一條邊比 v 出發的任意其他邊的操作次序都晚
(3)對於路徑上任意相鄰的兩條邊 e1,e2 ,如果它們有公共點 x ,那麼就 x 出發的所有邊中,e1 和 e2 的操作次序必須相鄰,並且 e1 先於 e2
不難發現產生的所有限制關係都在有公共點的兩條邊之間產生
同時,由於這是一棵樹,所以如果對於任意 u 都滿足 u 出發的任意邊之間都不會產生矛盾,那麼整棵樹都不會產生矛盾(因爲可以不斷刪葉子)
由於我們有兩條邊操作次序相鄰的限制,故可以對每個點,用並查集或鏈表維護連續段,對 i 確定位置 v 時判斷是否合法
如何判斷合法性:
(1)設 u 到 v 的路徑上第一條邊爲 e ,那麼需要滿足 e 是某個連續段的開頭,並且 u 出發的所有邊已經被合成一個連續段,或者 e 所在連續段的末尾沒有被欽定爲最後一次操作
(2)最後一條邊同理
(3)對於路徑上連續的兩條邊 e1,e2 ,需要滿足:
① e1 是某個連續段的末尾,e2 是某個連續段的開頭,且 e1 和 e2 不屬於同一連續段
② e1 沒有被欽定爲最後一次操作,並且 e2 沒有被欽定爲第一次操作
③ 如果 e1 所在連續段的開頭被欽定爲第一次操作,且 e2 所在連續段的末尾被欽定爲最後一次操作,那麼需要滿足以 u 出發的邊中,除了 e1 和 e2 各自所在的連續段之外,不能有其他的連續段
找到了對應的 v 時,需要把 u 到 v 的路徑上所有相鄰的兩條邊合併連續段,並把第一條邊欽定爲第一次操作,最後一條邊欽定爲最後一次操作
O(Tn2)
Code
#include<bits/stdc++.h>template<classT>inlinevoidread(T &res){
res =0;bool bo =0;char c;while(((c =getchar())<'0'|| c >'9')&& c !='-');if(c =='-') bo =1;else res = c -48;while((c =getchar())>='0'&& c <='9')
res =(res <<3)+(res <<1)+(c -48);if(bo) res =~res +1;}constint N =2005, M = N <<1;int n, a[N], ecnt, nxt[M], adj[N], go[M], res, d[N],
st[M], ed[M], fir[M], lst[M], par[N];bool ist[M], ied[M], vis[N];voidadd_edge(int u,int v){
nxt[++ecnt]= adj[u]; adj[u]= ecnt; go[ecnt]= v;
nxt[++ecnt]= adj[v]; adj[v]= ecnt; go[ecnt]= u;
d[u]++; d[v]++;}voiddfs(int u,int fu,int fe){if(ied[fe]&&(d[u]==1|| st[fe]!= fir[u])&& u < res &&!vis[u]) res = u;for(int e = adj[u], v; e; e = nxt[e]){if((v = go[e])== fu)continue;if(!ied[fe]||!ist[e]|| ed[e]== fe)continue;if(fir[u]== e || lst[u]== fe)continue;if(fir[u]== st[fe]&& lst[u]== ed[e]&& d[u]>2)continue;dfs(v, u, par[v]= e ^1);}}voidwork(){int x, y;read(n); ecnt =1;memset(adj,0,sizeof(adj));memset(vis,0,sizeof(vis));memset(fir,0,sizeof(fir));memset(lst,0,sizeof(lst));memset(ist,1,sizeof(ist));memset(ied,1,sizeof(ied));memset(d,0,sizeof(d));for(int i =1; i <= n; i++)read(a[i]);for(int i =1; i < n; i++)read(x),read(y),add_edge(x, y);for(int i =2; i <= ecnt; i++) st[i]= ed[i]= i;for(int i =1; i <= n; i++){int u = a[i]; res = n;for(int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])if(ist[e]&&(d[u]==1|| ed[e]!= lst[u]))dfs(v, u, par[v]= e ^1);printf("%d ", res); vis[res]=1;
lst[res]= par[res];int e = par[res]^1;for(int v = go[par[res]]; v != u; v = go[par[v]]){int f = par[v], l = st[f], r = ed[e];
st[r]= l; ed[l]= r; ied[f]= ist[e]=0;
e = f ^1; d[v]--;}
fir[u]= e;}puts("");}intmain(){int T;read(T);while(T--)work();return0;}