全序开题
准备打假
A.CF757G Can Bash Save the Day?
树上动态求 p l . . . p r p_l...p_r pl...pr到 x x x的距离和,支持交换 p x , p x + 1 p_x,p_{x+1} px,px+1
可持久化点分树,答案为第 r r r个版本上点分树的答案减去第 l − 1 l-1 l−1个版本上点分树的答案,修改就是对第 x x x个版本的单点修改。
因为可持久化需要把到儿子的链接全部复制(如果你写自底向上的点分树也一样需要把儿子到父亲的链接全部修改),所以儿子个数太多可持久化复杂度就没有保证,需要三度化原图后再进行点分治。
UPD:仔细想了一想,如果要维护 f a fa fa的可持久化数组的话,那么一次修改好像需要改整个子树,所以自底向上好像根本写不了的样子,就照可持久化线段树的写法写吧。
A C C o d e \mathcal AC \ Code AC Code
//11:06~12:12
//Can Bush Save the Day ?
#include<bits/stdc++.h>
#define maxn 400005
#define maxp maxn * 20
#define lim 30
#define LL long long
#define pb push_back
#define pii pair<int,LL>
#define mp make_pair
#define Fi first
#define Se second
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char cb[1<<16],*cs=cb,*ct=cb;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<16,stdin),cs==ct)?0:*cs++)
void read(int &res){
char ch;
for(;!isdigit(ch=getc()););
for(res=ch-'0';isdigit(ch=getc());res=res*10+ch-'0');
}
const int M = (1 << 30) - 1;
int n,q,a[maxn],cnt_p;
int sz[maxn],dep[maxn],rt[maxn],st[maxn],ed[maxn],id[maxp],tot;
LL ds[maxn][lim],Sm[maxp],Sz[maxp],Smf[maxp],Szf[maxp];
vector<pii >tE[maxn],E[maxn];
vector<int>G[maxp];
void dfs0(int u,int ff){
int p=0;
for(auto v:tE[u]) if(v.Fi^ff){
if(!p){
E[u].pb(mp(v.Fi,v.Se));
E[v.Fi].pb(mp(u,v.Se));
p = u;
}
else{
int t = ++cnt_p;
E[p].pb(mp(t,0));
E[t].pb(mp(p,0));
E[t].pb(mp(v.Fi,v.Se));
E[v.Fi].pb(mp(t,v.Se));
p = t;
}
dfs0(v.Fi,u);
}
}
void dfs1(int u,int ff,int tsz,int &mn,int &rt){
int mx = 0;sz[u] = 1;
for(auto v:E[u]) if(v.Fi^ff && !dep[v.Fi])
dfs1(v.Fi,u,tsz,mn,rt),sz[u]+=sz[v.Fi],mx=max(mx,sz[v.Fi]);
if((mx=max(mx,tsz-sz[u])) < mn)
mn = mx , rt = u;
}
int Gert(int u,int tsz){
int mn,rt;
dfs1(u,0,tsz,mn=0x3f3f3f3f,rt=-1);
return rt;
}
void dfs2(int u,int ff,LL dis,int d){
ds[u][d] = dis;sz[u] = 1;
for(auto v:E[u]) if(v.Fi^ff && !dep[v.Fi])
dfs2(v.Fi,u,dis+v.Se,d),sz[u]+=sz[v.Fi];
}
void Build(int u,int d){
dep[u] = d;
dfs2(u,0,0,d);
for(auto v:E[u]) if(!dep[v.Fi]){
int t = Gert(v.Fi,sz[v.Fi]);
G[u].pb(t);
Build(t,d+1);
}
}
void dfs3(int u){
static int tot = 0;
st[u] = ++tot;
for(int v:G[u]) dfs3(v);
ed[u] = tot;
}
void ins(int &u,int p,int d){
Sm[++tot] = Sm[u] + ds[p][d] , Sz[tot] = Sz[u] + 1 ,
Smf[tot] = Smf[u] + ds[p][d-1] , Szf[tot] = Szf[u] + 1;
G[tot] = G[u] , id[tot] = id[u];
u = tot;
for(int &v:G[u]) if(st[id[v]] <= st[p] && st[p] <= ed[id[v]])
ins(v,p,d+1);
}
LL qry(int u,int p,int d){
LL r = Sm[u] + Sz[u] * ds[p][d];
if(d>1) r -= Smf[u] + Szf[u] * ds[p][d-1];
for(int v:G[u]) if(st[id[v]] <= st[p] && st[p] <= ed[id[v]])
r += qry(v,p,d+1);
return r;
}
int main(){
// freopen("1.in","r",stdin);
read(n),read(q);
rep(i,1,n) read(a[i]);
rep(i,1,n-1){
int u,v,w;
read(u),read(v),read(w);
tE[u].pb(mp(v,w)),tE[v].pb(mp(u,w));
}
cnt_p = n;
dfs0(1,0);
Build(rt[0]=Gert(1,cnt_p),1);
dfs3(rt[0]);
rep(i,1,cnt_p) id[i] = i;tot=cnt_p;
rep(i,1,n) ins(rt[i]=rt[i-1],a[i],1);
LL ans = 0;
for(int t,a,b,c;q--;){
read(t);
if(t == 1){
read(a),read(b),read(c);
int l = (ans & M) ^ a , r = (ans & M) ^ b , v = (ans & M) ^ c;
ans = qry(rt[r],v,1) - qry(rt[l-1],v,1);
printf("%lld\n",ans);
}
else{
read(a);
int x = (ans & M) ^ a;
ins(rt[x]=rt[x-1],::a[x+1],1);
swap(::a[x],::a[x+1]);
}
}
}
CF650D Zip-line
每次询问将原序列的一个点的值改变后的最长严格上升子序列的长度。
那么修改后的最长上升子序列分为经过修改点和不经过的。
不经过的可以求出每个 a a a,最靠前的 b b b使得 a r r a > a r r b arr_a > arr_b arra>arrb,且 b b b结尾的最长上升子序列长度与 a a a开头的最长上升子序列长度之和为最长上升子序列长度。
然后对于 [ a + 1 , b − 1 ] [a+1,b-1] [a+1,b−1]区间取 m a x max max即可,可以用 s t st st表实现。
经过的可以用可持久化线段树简单维护。
可能对于不经过的有更优的其他做法。
UPD:不经过的可以通过判断是否每个LIS都经过这个点来得知答案是 L I S LIS LIS还是 L I S − 1 LIS-1 LIS−1,可以转化为:
假设这个点 x x x结尾的 L I S LIS LIS长度是 a a a,那么就是判断是否对于所有可能在全局 L I S LIS LIS中的点 y ( y ≠ x ) y(y\neq x) y(y=x),以 y y y结尾的 L I S LIS LIS长度是否是 a a a,如果是,则有 L I S LIS LIS不经过 x x x。
A C C o d e \mathcal AC \ Code AC Code
//12:20~12:52
//Zip-Line
#include<bits/stdc++.h>
#define maxn 400005
#define maxp maxn * 50
#define rep(i,j,k) for(int i=(j),LIM=(k);i<=LIM;i++)
#define per(i,j,k) for(int i=(j),LIM=(k);i>=LIM;i--)
using namespace std;
char start;
int n,m,a[maxn],b[maxn];
int h[maxn],sb[maxn<<1],rt[2][maxn],f[2][maxn],lc[maxp],rc[maxp],mx[maxp],in[maxn],tot;
vector<int>G[maxn];
void ins(int &u,int l,int r,int p,int v){
lc[++tot] = lc[u] , rc[tot] = rc[u] , mx[tot] = max(mx[u] , v);
u = tot;
if(l == r) return;
int m = l+r