• SP2666 QTREE4


    题意翻译

    你被给定一棵n个点的带边权的树(边权可以为负),点从1到n编号。每个点可能有两种颜色:黑或白。我们定义dist(a,b)为点a至点b路径上的权值之和。

    一开始所有的点都是白色的。

    要求作以下操作:

    C a 将点a的颜色反转(黑变白,白变黑)

    A 询问dist(a,b)的最大值。a,b点都必须为白色(a与b可以相同),显然如果树上仍存在白点,查询得到的值一定是个非负数。

    特别地,如果作'A'操作时树上没有白点,输出"They have disappeared."。

    题解

    一道神仙题……完全不知道这题大佬们是怎么想出思路的orz这里

    然而还是看不太懂决定自己写一下……

    本来打算用捉迷藏一样做的……但发现我那一道写的时候是用括号表示做的……这题有边权不能做……

    说下思路吧

    LCT只能维护点权,但本题中是边权

    我们可以指定一个根(比如1),然后令所有的边权变成它指向的儿子的点权,记为$len$

    ps:这样做之后会有很多细节,需要一一注意

    然后在考虑一下如何求出两个相邻最远的白点的距离呢?

    因为LCT上面有实边和虚边,所以实儿子和虚儿子的信息要用不同的方法维护

    实边构成了一个splay,我们定义几个数组,$lmx$表示splay中深度最浅的点能够到达的最远的白点的距离,$rmx$表示splay中深度最深的点能够到达的最远的白点的距离,$mxs$表示splay中距离最远的两个白点的距离,也就是答案,$sum$表示整棵splay的长度和

    ps:LCT中的splay在原树中是一条链!所以$sum$维护的是整条链的长度和,同理$lmx$表示这条链的顶端距离白点的最远距离,$rmx$表示这条链的底端距离白点的最远距离

    虚儿子的信息怎么维护?虚儿子我们需要记录的只有到白点的最远距离,以及虚儿子中的$mxs$,直接开两个$set$丢进去就好了

    那么,这些信息够了吗?

    当然已经够了,因为已经足够将信息不断向上传递了

    先考虑一下虚子树对$mxs$的影响,实边太复杂了待会儿再说

    因为在$access$的时候,会有换边的操作

    对于由实变虚的原右儿子,把$mx[rs]$丢进路径的$set$里,把$lx[rs]$丢进链的$set$里

    同理,把由虚变实的新右儿子$mx[y]$和$lx[y]$从对应的$set$里删除就行

    $mx$丢进去我们可以理解,因为要维护答案,但为什么链的长度只要把$lx$丢进$set$就行了呢?

    考虑一下,$set$里维护的是这一个顶点到其虚子树中的白点的最大距离,而$lx[rs]$代表的是$x$的右子树中深度最浅的点到白点的最大距离,如果回到原树上,这就是$x$的儿子到白点的最大距离!!又因为我们将边权给了儿子,所以,只需要记录虚边的$ls$即可

    然后期待已久(写到吐血)pushup操作了

    这部分还是具体看代码好了……细节太多了……我这里简单提几嘴

    $lmx$要过子树的最低点,所以等于$max(lmx[ls],max(虚链中最长+整个左子树,右子树中最长+整个左子树))$

    $rmx$同理

    然后$mxs$就是把所有可能的链给接起来以及子树的答案都弄出来更新就好了

    ps:如果$x$也是白点,他自己也得拿出来更新$mxs$

    时间复杂度$O(n log^2 n)$

    然而如果$set$的$size$大说明虚边多,深度不大,LCT操作次数少一点

    如果树的深度深,$set$的$size$又不会太大,所以时间复杂度不是很严格

    ps:请务必详细看看注解,这题细节多的要命

      1 //minamoto
      2 #include<bits/stdc++.h>
      3 using namespace std;
      4 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
      5 char buf[1<<21],*p1=buf,*p2=buf;
      6 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
      7 inline int read(){
      8     #define num ch-'0'
      9     char ch;bool flag=0;int res;
     10     while((ch=getc())<'0'||ch>'9')
     11     (ch=='-')&&(flag=true);
     12     for(res=num;(ch=getc())>='0'&&ch<='9';res=res*10+num);
     13     (flag)&&(res=-res);
     14     #undef num
     15     return res;
     16 }
     17 const int N=200005,inf=0x3f3f3f3f;
     18 int n,ver[N],Next[N],head[N],edge[N],tot,ans=-inf,col[N];
     19 inline void add(int u,int v,int e){ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;}
     20 inline int fir(multiset<int> &s){return s.size()?*s.rbegin():-inf;}
     21 inline int sec(multiset<int> &s){return s.size()>1?*(++s.rbegin()):-inf;}
     22 /*分别是查询set里的最大和次大*/
     23 #define ls ch[x][0]
     24 #define rs ch[x][1]
     25 int ch[N][2],fa[N],lmx[N],rmx[N],mxs[N],sum[N],len[N],w[N];
     26 /*w表示这个点是否是白点,如果是赋值为0,否则赋值为-inf*/ 
     27 multiset<int> chain[N],path[N];
     28 /*chain存链长,path存路径长*/
     29 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
     30 void init(){for(int i=0;i<=n;++i) lmx[i]=rmx[i]=mxs[i]=-inf;}
     31 void pushup(int x){
     32     sum[x]=sum[ls]+sum[rs]+len[x];
     33     /*sum表示整棵splay的长度和*/ 
     34     int cha=max(w[x],fir(chain[x]));
     35     /*找最远的虚边上的白点的距离*/ 
     36     int L=max(cha,rmx[ls]+len[x]);
     37     /*向左子树或向虚边最远能走多远
     38     ps:这里len要加上去,因为左子树在原树中是x的祖先
     39     要加上x到父亲的距离(已经被转化为点权了)*/
     40     int R=max(cha,lmx[rs]);
     41     /*向右子树或向虚边最远能走多远*/
     42     lmx[x]=max(lmx[ls],sum[ls]+len[x]+R);
     43     /*不经过x,或经过x并走到最远
     44     注意左子树在原树中是一条链!不存在路径重叠问题*/
     45     rmx[x]=max(rmx[rs],sum[rs]+L);
     46     /*同理*/ 
     47     mxs[x]=max(rmx[ls]+len[x]+R,lmx[rs]+L);
     48     cmax(mxs[x],max(mxs[ls],mxs[rs]));
     49     cmax(mxs[x],fir(path[x]));
     50     cmax(mxs[x],fir(chain[x])+sec(chain[x]));
     51     /*最长链和次长链可以形成一条路径*/ 
     52     if(w[x]==0) cmax(mxs[x],max(fir(chain[x]),0));
     53     /*用一堆东西来更新答案*/
     54 }
     55 void rotate(int x){
     56     int y=fa[x],z=fa[y],d=ch[y][1]==x;
     57     if(!isroot(y)) ch[z][ch[z][1]==y]=x;
     58     fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y);
     59 }
     60 void splay(int x){
     61     for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){
     62         if(!isroot(y))
     63         ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y);
     64         rotate(x);
     65     }
     66     pushup(x);
     67 }
     68 void access(int x){
     69     for(int y=0;x;x=fa[y=x]){
     70         splay(x);
     71         if(rs) path[x].insert(mxs[rs]),chain[x].insert(lmx[rs]);
     72         if(y) path[x].erase(path[x].find(mxs[y])),chain[x].erase(chain[x].find(lmx[y]));
     73         rs=y,pushup(x);
     74         /*注意虚实子树变换时要更新path和chain*/
     75     }
     76 }
     77 void modify(int x){
     78     /*改变点的颜色,col为1表示黑,0表示白*/
     79     access(x),splay(x);
     80     col[x]^=1,w[x]=col[x]?(-inf):0;
     81     pushup(x),ans=mxs[x];
     82 }
     83 void dfs(int u){
     84     for(int i=head[u];i;i=Next[i]){
     85         int v=ver[i];
     86         if(v==fa[u]) continue;
     87         fa[v]=u,len[v]=edge[i],dfs(v);
     88         /*把边的权值给儿子*/
     89         chain[u].insert(lmx[v]),path[u].insert(mxs[v]);
     90     }
     91     pushup(u);
     92 }
     93 int main(){
     94     //freopen("testdata.in","r",stdin);
     95     n=read();init();
     96     for(int i=1;i<n;++i){
     97         int u=read(),v=read(),e=read();
     98         add(u,v,e),add(v,u,e);
     99     }
    100     dfs(1),ans=mxs[1];int q=read();
    101     while(q--){
    102         char op=getc();int x;
    103         getc();
    104         if(op=='A'){
    105             ans<0?puts("They have disappeared."):printf("%d
    ",ans);
    106         }
    107         else x=read(),modify(x);
    108     }
    109     return 0;
    110 }
  • 相关阅读:
    sqlite遇到database is locked问题的完美解决
    Delphi使程序的窗口出现在最前面并激活
    win10 家庭版不支持gpedit.msc的解决办法
    Delphi Record To Stream
    SQL Server 查看CPU情况
    JavaScript 获取 Url 上的参数(QueryString)值
    关于EF分页查询报错(Count must have a non-negative value.)的解决方案
    调用微信退款接口时出现System.Security.Cryptography.CryptographicException: 出现了内部错误 解决办法
    Javascript中“==”与“===”的区别
    IE浏览器各版本的CSS Hack
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9426822.html
Copyright © 2020-2023  润新知