QAQ的LIS树 QAQ的LIS树2 题解报告
这两道题实际上考试的时候是一道题OwO
太可怕了,忙了我三个多小时,写了整整7K
这个题两个询问关联性不强,所以分开来考虑
QAQ的LIS树
考虑如何用dp求解答案
设dp(v)表示v到根的修改后的序列的和,c(v)是v点点权
那么v的答案就是dp(v)减去v到根的点权和
最直观的想法是我们从v点向上暴力跳父亲,跳到第一个不用修改的点u
不难发现因为u没有修改,所以之后的序列和u之后的序列是完全一样的
可以直接转移给v了,那么dp(v)的值就是dp(u)和v->u的序列和了
注意到v->u的所有点我们一定会修改,所以其序列形式一定是c(v),c(v)+1,c(v)+2……
这个数列的和直接等差数列求和即可
准确描述一下u如果要修改应该要改成c(v)+dep(v)-dep(u)
如果u不用修改则满足c(u)>c(v)+dep(v)-dep(u)
移项之后得c(u)+dep(u)>c(v)+dep(v),即找到第一个满足这个条件的点
那么找到第一个不用修改的u的方法就很显然了,我们处理出倍增数组倍增即可
考虑修改的影响,不难发现一个修改影响到的是一棵子树,如果我们暴力重构子树,时间复杂度就爆炸了
我们可以考虑树分块,将树分成若干个块,我们定义这个块中深度最小的点为这个块的根(可以证明这样的点每个块有且仅有一个)
我们对于每个点维护四个信息
第一个信息是这个点到这个块的根的修改后的序列的和
第二个信息是这个点到根修改后根的权值(不难发现这个信息也满足dp性质)
第三个信息是这个点到根的点权和
第四个信息是这个点向上的倍增数组
考虑每次查询块与块之间的拼接
因为我们记录的第二个信息,当跳到上面的块的时候,我们很容易确定当前点拼接到上一个块之后是否需要修改
如果不需要修改,那么直接继续跳到当前块的根即可
否则我们不难得到当前这个点需要修改到的值,同上述的方法倍增找到第一个不需要修改的点并更新答案即可
最后用修改后的序列的和减去点权和就是答案了
考虑修改的影响
每次修改我们只需要暴力重构块内的信息就可以了
对于第三个信息一遍DFS可以搞定,第四个信息重构倍增数组
第一个和第二个信息利用重构后的倍增数组可以重新计算
这样我们就在O(m*sqrt(n)*log(n))的时间内解决了这个问题
但是常数较小,所以跑得还是很快的
QAQ的LIS树2
听何神说这个题目可以用离线+线段树合并搞一搞,但是蒟蒻智商低,并没有听懂
所以自己YY了一个可以在线的O(nlog^2n)的做法
这个询问比上个询问要好想一点
我们考虑一棵子树怎么样才是合法的,当且仅当这棵子树的所有边都是存在的
但是我们如果用0/1表示存在性的话,查询全局的可行解的最大值就变得非常的麻烦
我们不妨将所有不合法的解都变成不可能在查询最大值时查询到的数值,那么我们就可以直接查询最大值了
做法是这样的,我们给每条边都赋一个大于n的权值,每个点一开始都是自己的子树大小
当一条边从合法变成不合法的时候,我们将这条边上面的所有点都减去这个权值
当一条边从不合法变成合法的时候,我们将这条边上面的所有点都加上这个权值
不难发现,一个点的权值是正整数当且仅当子树内所有边均合法
否则因为sz<=n,只要有一条边不合法,其上面所有的点都会减去一个>n的数值变成负数
这用树链剖分+线段树是易于维护的
那么查询就异常的简单了,我们只需要求一下全局的最大值即可(因为不合法的解不可能作为最优解)
对于修改,我们发现每次修改因为修改的是点权,所以影响到的不只是一条边
但是我们会发现我们可以把影响到的边分成两类,一类是当前点到儿子的边,另一类是当前点到父亲的边
不难发现第一类边虽然可能有很多,但是影响到的点都是一样的
我们只需要算出这些影响的综合就可以了,也就是要查询更改前和更改后不合法的边的权值和
不合法的边一定满足c(v)>=c(u),我们对于每个点以c为键值用平衡树维护儿子的信息即可
这样每次在平衡树上直接查就可以了,顺便更改掉其父亲对应的平衡树
之后再判断当前点到父亲的边是否发生了变化并相应的进行修改就可以了
时间复杂度O(nlog^2n)
贴下代码咯,两道题目二合一强行用namespace凑起来
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
const int maxn=100010;
const LL oo=1LL<<60;
int n,m,f,u,v,blo;
int h[maxn],cnt=0;
int fa[maxn],top[maxn],son[maxn];
int w[maxn],pos[maxn],fp[maxn],tot=0;
int co[maxn],rt[maxn];
bool vis[maxn];
int dep[maxn];
struct edge{
int to,next;
}G[maxn];
LL c[maxn],sz[maxn];
LL add[maxn<<2];
LL mx[maxn<<2];
LL xp[maxn];
void Add(int x,int y){++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;}
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
struct Splay_Tree{
int fa[maxn],C[maxn][2];
LL s[maxn],val[maxn],V[maxn];
#define fa(i) fa[i]
#define c(x,i) C[x][i]
#define s(i) s[i]
#define val(i) val[i]
void up(int u){
if(!u)return;
s(u)=s(c(u,0))+s(c(u,1))+val(u);
}
void rotate(int p,int x){
int mark= p==c(x,1),y=c(p,mark^1),z=fa(x);
if(c(z,0)==x)c(z,0)=p;
if(c(z,1)==x)c(z,1)=p;
if(y)fa(y)=x;
fa(p)=z;c(p,mark^1)=x;fa(x)=p;c(x,mark)=y;
up(x);
}
void Splay(int p,int k,int &rt){
while(fa(p)!=k){
int x=fa(p),y=fa(x);
if(y==k)rotate(p,x);
else if(p==c(x,0)^x==c(y,0))rotate(p,x),rotate(p,y);
else rotate(x,y),rotate(p,x);
}up(p);if(!k)rt=p;
}
void insert(int u,int &rt){
int now=rt,f=now;
while(now){
f=now;
if(c[u]>V[now])now=c(now,1);
else now=c(now,0);
}
fa(u)=f;V[u]=c[u];val(u)=s(u)=co[u];
if(f)c(f,c[u]>V[f])=u;
Splay(u,0,rt);
}
void del(int u,int &rt){
Splay(u,0,rt);
if(!c(u,0)){rt=c(u,1);fa(rt)=0;c(u,1)=0;return;}
int now=c(u,0);
while(c(now,1))now=c(now,1);
Splay(now,u,rt);
fa(now)=0;c(u,0)=0;rt=now;
fa(c(u,1))=now;c(now,1)=c(u,1);c(u,1)=0;
up(now);
}
int Get_pre(LL v,int &rt){
int now=rt,p=-1;
while(now){
if(V[now]<v)p=now,now=c(now,1);
else now=c(now,0);
}return p;
}
LL ask(LL v,int &rt){
int pre=Get_pre(v,rt);
if(pre==-1)return s(rt);
Splay(pre,0,rt);
return s(c(rt,1));
}
void DFS(int u){
if(!u)return;
DFS(c(u,0));
printf("%lld ",V[u]);
DFS(c(u,1));
}
}T;
namespace S{
void DFS(int u,int f){
w[u]=1;sz[u]=co[u];
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f)continue;
dep[v]=dep[u]+1;
DFS(v,u);w[u]+=w[v];sz[u]+=sz[v];
if(w[son[u]]<w[v])son[u]=v;
}return;
}
void Get_pos(int u,int f){
top[u]=f;pos[u]=++tot;fp[tot]=u;
if(!son[u])return;
Get_pos(son[u],f);
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f||v==son[u])continue;
Get_pos(v,v);
}return;
}
void Get_Splay(int u,int f){
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f)continue;
T.insert(v,rt[u]);
Get_Splay(v,u);
}return;
}
void build(int o,int L,int R){
if(L==R){mx[o]=sz[fp[L]];return;}
int mid=(L+R)>>1;
build(o<<1,L,mid);build(o<<1|1,mid+1,R);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
void push_down(int o,int l,int r){
mx[l]+=add[o];add[l]+=add[o];
mx[r]+=add[o];add[r]+=add[o];
add[o]=0;
}
void UPD(int o,int L,int R,int x,int y,LL v){
if(L>=x&&R<=y){
mx[o]+=v;add[o]+=v;
return;
}
int mid=(L+R)>>1;
int l=(o<<1),r=(l|1);
if(add[o]!=0)push_down(o,l,r);
if(y<=mid)UPD(o<<1,L,mid,x,y,v);
else if(x>mid)UPD(o<<1|1,mid+1,R,x,y,v);
else UPD(o<<1,L,mid,x,y,v),UPD(o<<1|1,mid+1,R,x,y,v);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
void Get_add(int u,LL v){
while(u){
UPD(1,1,n,pos[top[u]],pos[u],v);
u=fa[top[u]];
}return;
}
void Get_modify(int u,LL v){
LL now=T.ask(v,rt[u])-T.ask(c[u],rt[u]);
UPD(1,1,n,pos[u],pos[u],now);
if(fa[u]){
if(c[u]>=c[fa[u]])now-=co[u];
if(v>=c[fa[u]])now+=co[u];
T.del(u,rt[fa[u]]);c[u]=v;
T.insert(u,rt[fa[u]]);
Get_add(fa[u],now);
}c[u]=v;return;
}
};
namespace C{
int anc[maxn][10];LL mx[maxn][20];
int rt[maxn],sz[maxn],dep[maxn];
LL go[maxn],co[maxn],s[maxn];
void Get_block(int u,int f){
if(sz[rt[f]]==blo)rt[u]=u,sz[u]=1;
else sz[rt[f]]++,rt[u]=rt[f];
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f)continue;
dep[v]=dep[u]+1;
Get_block(v,u);
}return;
}
void Get_pre(int u,int v){
int now=dep[u]-dep[v];
anc[u][0]=fa[u];mx[u][0]=c[u]+dep[u];
for(int i=1;(1<<(i-1))<=now;++i){
if(anc[u][i-1]!=-1){
int a=anc[u][i-1];
anc[u][i]=anc[a][i-1];
mx[u][i]=max(mx[u][i-1],mx[a][i-1]);
}
}return;
}
int Get_mx(int u,int v,int w){
int log,now=dep[u]-dep[v];
for(log=0;(1<<log)<=now;++log);--log;
for(int i=log;i>=0;--i){
if(anc[u][i]!=-1&&mx[u][i]<=w)u=anc[u][i];
}
if(c[u]+dep[u]<=w)return -1;
if(dep[u]<dep[v])return -1;
return u;
}
void Get_ans(int u){
int p=Get_mx(u,rt[u],c[u]+dep[u]);
if(p==-1){
go[u]=c[u]*(dep[u]-dep[rt[u]]+1)+xp[dep[u]-dep[rt[u]]];
co[u]=c[u]+dep[u]-dep[rt[u]];
}else{
go[u]=go[p]+c[u]*(dep[u]-dep[p])+xp[dep[u]-dep[p]-1];
co[u]=co[p];
}return;
}
void DFS_block(int u,int f){
Get_pre(u,rt[u]);Get_ans(u);
if(rt[f]==rt[u])s[u]=s[f]+c[u];
else s[u]=c[u];
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f||rt[v]!=rt[u])continue;
DFS_block(v,u);
}return;
}
LL Get_cost(int u){
LL ans=go[u]-s[u],la=co[u];
u=fa[rt[u]];
while(u){
if(la<c[u])ans=ans+go[u],la=co[u];
else{
int p=Get_mx(u,rt[u],la+1+dep[u]);
if(p==-1){
ans=ans+(la+1)*(dep[u]-dep[rt[u]]+1)+xp[dep[u]-dep[rt[u]]];
la=la+1+dep[u]-dep[rt[u]];
}else{
ans=ans+go[p]+(la+1)*(dep[u]-dep[p])+xp[dep[u]-dep[p]-1];
la=co[p];
}
}ans-=s[u];u=fa[rt[u]];
}return ans;
}
}; int main(){
freopen("increasing.in","r",stdin);
freopen("increasing.out","w",stdout);
int __size__=128<<20;
char *__p__=(char*)malloc(__size__)+__size__;
__asm__("movl %0, %%esp\n"::"r"(__p__));
read(n);blo=150;
memset(C::anc,-1,sizeof(C::anc));
xp[0]=0;
for(int i=1;i<=n;++i)xp[i]=xp[i-1]+i;
for(int i=2;i<=n;++i){
read(f);f++;
Add(f,i);fa[i]=f;
}
for(int i=1;i<=n;++i)co[i]=rand()%n+n,co[i]=-co[i];
S::DFS(1,-1);S::Get_pos(1,1);
for(int i=1;i<=n;++i)sz[i]+=w[i],sz[i]-=co[i];
S::build(1,1,n);S::Get_Splay(1,-1);
C::sz[0]=blo;C::Get_block(1,0);
for(int i=1;i<=n;++i)if(C::rt[i]==i)C::DFS_block(i,fa[i]);
read(m);
while(m--){
char ch=getchar();
while(ch<'!')ch=getchar();
if(ch=='M'){
read(u);read(v);u++;
S::Get_modify(u,c[u]+v);
C::DFS_block(u,fa[u]);
}else if(ch=='S')printf("%lld\n",mx[1]);
else{
read(u);u++;
printf("%lld\n",C::Get_cost(u));
}
}return 0;
}
UPD:被梁大爷虐傻了,感觉自己智商被掏空
第一题做法简直是在扯淡OwO有更优秀的做法
一直以为信息是不可合并的,才采用树分块的鬼畜做法
但是信息是可合并的,直接树链剖分可以了
QAQ的LIS树 QAQ的LIS树2 题解报告的更多相关文章
- NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】
NOIP 2013 货车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在 ...
- 洛谷P4092 [HEOI2016/TJOI2016]树 并查集/树链剖分+线段树
正解:并查集/树链剖分+线段树 解题报告: 传送门 感觉并查集的那个方法挺妙的,,,刚好又要复习下树剖了,所以就写个题解好了QwQ 首先说下并查集的方法趴QwQ 首先离线,读入所有操作,然后dfs遍历 ...
- 洛谷P2414 阿狸的打字机 [NOI2011] AC自动机+树状数组/线段树
正解:AC自动机+树状数组/线段树 解题报告: 传送门! 这道题,首先想到暴力思路还是不难的,首先看到y有那么多个,菜鸡如我还不怎么会可持久化之类的,那就直接排个序什么的然后按顺序做就好,这样听说有7 ...
- 【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)
http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更 ...
- 【bzoj3083】遥远的国度 树链剖分+线段树
题目描述 描述zcwwzdjn在追杀十分sb的zhx,而zhx逃入了一个遥远的国度.当zcwwzdjn准备进入遥远的国度继续追杀时,守护神RapiD阻拦了zcwwzdjn的去路,他需要zcwwzdjn ...
- (2016北京集训十三)【xsy1532】网络战争 - 最小割树+树上倍增+KD树
题解: 好题!! 这题似乎能上我代码长度记录的前五? 调试时间长度应该也能上前五QAQ 首先题目要求的明显就是最小割,当然在整个森林上求Q次最小割肯定是会GG的,所以我们需要一个能快速求最小割的算法— ...
- 【BZOJ-2325】道馆之战 树链剖分 + 线段树
2325: [ZJOI2011]道馆之战 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 1153 Solved: 421[Submit][Statu ...
- poj 2104 K-th Number (划分树入门 或者 主席树入门)
题意:给n个数,m次询问,每次询问L到R中第k小的数是哪个 算法1:划分树 #include<cstdio> #include<cstring> #include<alg ...
- 从B 树、B+ 树、B* 树谈到R 树
从B 树.B+ 树.B* 树谈到R 树 作者:July.weedge.Frankie.编程艺术室出品. 说明:本文从B树开始谈起,然后论述B+树.B*树,最后谈到R 树.其中B树.B+树及B*树部分由 ...
随机推荐
- vue之cli脚手架安装和webpack-simple模板项目生成
ue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目. GitHub地址是:https://github.com/vuejs/vue-cli 一.安 ...
- 安装coreseek与sphinx遇见的问题
1.问题 using config file 'etc/csft.conf'...indexing index 'xml'...WARNING: source 'xml': xmlpipe2 supp ...
- POJ--1936 All in All(水题,暴力即可)
All in All Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 30543 Accepted: 12723 Descript ...
- Java基础类编程集锦
1.计算1+2+3+4+5+6+7+8+9的值 package com.neusoft.chapter1; /** * @author zhao-chj *题:计算1+2+3+4+5+6+7+8+9的 ...
- 《机器学习实战》中的程序清单2-1 k近邻算法(kNN)classify0都做了什么
from numpy import * import operator import matplotlib import matplotlib.pyplot as plt from imp impor ...
- Codeforces 752C - Santa Claus and Robot - [简单思维题]
题目链接:http://codeforces.com/problemset/problem/752/C time limit per test 2 seconds memory limit per t ...
- MySQL InnoDB的存储结构总结
从物理意义上来讲,InnoDB表由共享表空间.日志文件组(redo文件组).表结构定义文件组成.若将innodb_file_per_table设置为on,则系统将为每一个表单独的生成一个table_n ...
- 网络层 运输层 NAT路由器 NATP路由器 一根光纤 多个固定ip
长城宽带想要公网和固定ip的来看看吧.[迅雷赚钱宝吧]_百度贴吧 https://tieba.baidu.com/p/4035318158?red_tag=0898389066 连续电话投诉加咨询一周 ...
- grafana,graphite,influxdb with docker
++++++++++++++++++++++++ sudo docker pull tutum/influxdb sudo docker run -d -p 8083:8083 -p8086:8086 ...
- webstorm添加调试nodejs
打开run菜单选择Edit Configurations 展开defaults菜单,选择nodejs 点击+按钮,选择Node.js,出现下面弹出框. 点击ok保存