我可是现在才去订正今年ZJOI的蒟蒻。
回想ZJOI2017Round2,我2小时过了T1大样例中k=2的点,然后半小时码完T2的20分暴力,接着2小时15分钟一直沉浸在子串l..r的后缀是i..n的幻觉中,最后15分钟暴力+卡常。
倘若我记得特判T1n=1的情况,又把T3的2小时拿去写T2的r=n的部分分(当时应该会做),或许就110分了?
但那都过去了。
吉司机是公布过官方题解的,但那比较难懂,我提几个细节。
1.lc[x]
表示x到根的链上有多少个“左儿子”,等价于x到根的左链上右兄弟的个数(注意是兄弟,根节点没有兄弟)
2.ls[x]
表示x到根的链上所有右兄弟的深度和
3.求答案时,和求两点间距离一样,先求出所有点的深度之和,再减去lca的深度之和。
记u是询问点,x是链的底端,y是链的顶端,v=lca(u,x)
如果dep[y]
注意x到y不一定一直是左儿子或一直是右儿子,并且最多拐一次弯。
如果u在拐角处上面,那么v的一个儿子会给答案多贡献,要减掉,因为按照前述做法,会以为这个儿子a,是v的另一个儿子b
#include
#include
const int N=400010;
typedef long long ll;
inline char read() {
static const int IN_LEN = 1000000;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
template<class T>
inline void read(T &x) {
static bool iosig;
static char c;
for (iosig = false, c = read(); !isdigit(c); c = read()) {
if (c == '-') iosig = true;
if (c == -1) return;
}
/* 这里这么写的原因见下 */
for (x = 0; isdigit(c); c = read())
x = (x + (x << 2) << 1) + (c ^ '0');
if (iosig) x = -x;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN], *oh = obuf;
inline void print(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void print(T x) {
static int buf[30], cnt;
if (x == 0) {
print('0');
} else {
if (x < 0) print('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) print((char)buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
int lson[N],rson[N],top[N],n,lc[N],rc[N],dep[N],m,u,l,r,rt,lp[N],rp[N],xb,id[N],dad[N],z;
bool ma[N];
ll ls[N],rs[N],ans;
void dfs(int&x,int fa,int l,int r,bool left){
x=++xb,lp[x]=l,rp[x]=r,dep[x]=dep[fa]+1,dad[x]=fa;
lc[x]=lc[fa]+left,ls[x]=ls[fa]+left*dep[x];
rc[x]=rc[fa]+!left,rs[x]=rs[fa]+(!left)*dep[x];
if(l==r)id[l]=x;
else{
int m;read(m);
ma[x]=m-l+1>r-m;
dfs(lson[x],x,l,m,1);
dfs(rson[x],x,m+1,r,0);
}
}
void dfs2(int x){
if(ma[x])top[lson[x]]=top[x],top[rson[x]]=rson[x];
else top[rson[x]]=top[x],top[lson[x]]=lson[x];
if(lson[x])dfs2(lson[x]),dfs2(rson[x]);
}
inline int lca(register int u,register int v){
register int t;
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])t=u,u=v,v=t;
v=dad[top[v]];
}
return dep[u]>dep[v]?v:u;
}
inline void lpath(register int x,register int y){//dep[x]>dep[y]
ans+=1ll*(lc[x]-lc[y])*dep[u]+ls[x]-ls[y];
register ll z=0;
register int v=lca(u,x),t=v;
if(dep[v]else z=v!=x && lp[rson[v]]<=lp[u] && rp[u]<=rp[rson[v]];
z+=1ll*(lc[x]-lc[t])*dep[v]+ls[t]-ls[y]-(lc[t]-lc[y]);
ans-=z<<1;
}
inline void rpath(register int x,register int y){
ans+=1ll*(rc[x]-rc[y])*dep[u]+rs[x]-rs[y];
register ll z=0;
register int v=lca(u,x),t=v;
if(dep[v]else z=v!=x && lp[lson[v]]<=lp[u] && rp[u]<=rp[lson[v]];
z+=1ll*(rc[x]-rc[t])*dep[v]+rs[t]-rs[y]-(rc[t]-rc[y]);
ans-=z<<1;
}
int main(){
//freopen("segment.in","r",stdin);
//freopen("2.txt","w",stdout);
read(n);
dfs(rt,0,1,n,0);
top[1]=1;
dfs2(rt);
read(m);
while(m--){
read(u),read(l),read(r);
if(l==1 && r==n){
print(dep[u]-1);
print('\n');
continue;
}
ans=0;
if(l==1)rpath(id[r+1],1);
else if(r==n)lpath(id[l-1],1);
else{
z=lca(id[l-1],id[r+1]);
lpath(id[l-1],lson[z]);
rpath(id[r+1],rson[z]);
}
print(ans),print('\n');
}
flush();
return 0;
}
树剖lca加IO优化,似乎在uoj上是rk2?