NOIP2018

打印 上一主题 下一主题

主题 834|帖子 834|积分 2502

P5024 [NOIP2018 提高组] 保卫王国

一颗树有 \(n\) 个点 \((1 \le n \le 10^5)\) , 带点权 。
给出 \(m\) 个询问 \((1 \le m \le 10^5)\) ,对于每个询问 ,你需要在树上选若干个点,满足如下条件:

  • 树上每一条边所连的两个节点中有至少一个被选中;
  • 限制询问相关的两个点必须被选中或不被选中;
要求你回答在满足上述条件的前提下,所选点点权的和的最小值,若无法满足上述条件则输出 \(-1\) 。
第一眼:要是没有条件 2 ,这就是一个骡的极小点覆盖集,再加上有 \(10^5\) 个询问,所以必然要使用倍增(或树剖)预处理。
考虑先不顾条件 2 ,考虑每个子树的根节点是否选的情况,用深搜做一遍子树点权和的DP;(然后就不晓得做了,果断贺 TJ )
再进行一次深搜,处理出全树点权和减去当前子树点权和(同样也需考虑子树的根节点是否选的情况)
核心:随后处理出 \(ldp[g][0/1][0/1]\) ,意为节点 u 与距其 \(2^n\) 的祖先所构成的链与其上的子树的最小权值和(后两个下标为判断链的两端是否被选)

(如图,深搜第一次处理蓝色部分,第二次处理绿色部分,随后预处理链的情况,预计最多需 \(2 \log n\) 条小链构成图中红色的长链)
处理后分别解决每个询问,用 \(LCA\) 计算即可。时间复杂度 \(O(n \log n)\)
总结:这样的水体虽然是码农题,但是写一天实在是太过分了,要提高效率
code[code]#include #define ll long long#define rgi register intusing namespace std;const int M=1e5+7;const int inf=1e11+7;inline int read(){        int w=0,r=1;char c=getchar();        while(!(isdigit(c)||c=='-'))c=getchar();        if(c=='-')r=-1,c=getchar();        while(isdigit(c))w=w*10+c-'0',c=getchar();        return w*r;}void gitgud(){        int ans=0;        for(int i=1;i=0;i--)                if(up[u2]!=up[u1]){                        sta1[0]=min(ldp[u1][0][0]+num1[0],ldp[u1][0][1]+num1[1]);                        sta1[1]=min(ldp[u1][1][0]+num1[0],ldp[u1][1][1]+num1[1]);                        num1[0]=sta1[0],num1[1]=sta1[1];                        sta2[0]=min(ldp[u2][0][0]+num2[0],ldp[u2][0][1]+num2[1]);                        sta2[1]=min(ldp[u2][1][0]+num2[0],ldp[u2][1][1]+num2[1]);                        num2[0]=sta2[0],num2[1]=sta2[1];                        u1=up[u1],u2=up[u2];                }//        printf("st:%d %d %d %d  %lld %lld\n",u1,s1,u2,s2,num1[s1],num2[s2]);        int lca=up[0][u1];        int an1=dp[0][lca]-dp[1][u1]-dp[1][u2]+num1[1]+num2[1]+ddp[0][lca];        int an2=dp[1][lca]-min(dp[0][u1],dp[1][u1])+min(num1[0],num1[1])-min(dp[0][u2],dp[1][u2])+min(num2[0],num2[1])+ddp[1][lca];//        printf("ed:%d %d %d %lld %lld    %lld %lld %lld %lld\n",lca,u1,u2,an1,an2,num1[0],num1[1],num2[0],num2[1]);        return max(an1,an2);}int main(){//  freopen("text.in","r",stdin);//        freopen("text.out","w",stdout);    n=read(),m=read(),read();    for(int i=1;i

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

前进之路

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表