zoukankan      html  css  js  c++  java
  • [BZOJ 4071] 巴邻旁之桥

    Link:

    BZOJ 4071传送门

    Solution:

    首先算出能提前算的贡献

    $K=1$:肯定选中间的点,小学数学

    $K=2$:对于每对$(x,y)$一定选离$(x+y)/2$近的桥

    也就是说将$(x,y)$按$(x+y)/2$的值排序后一定恰有一个分割点使得两边选择不同的桥!

    考虑如何如何快速枚举所有分割点时的答案:

    需要支持插入、删除、求中位数及两边的和,明显选择$Splay$来维护(求和更容易些)

    这样先将所有数对加进一个$Splay$,再不断将其中的数移向另一棵$Splay$即可

    注意:可能以前的$Splay$板子容易出不少锅啊……

    首先$Update$时最好对$z$进行判断,然后$Splay$最后也要$Pushup$否则会在树高低时不更新$size$

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define X first
    #define Y second
    typedef double db;
    typedef long long ll;
    typedef pair<int,int> P;
    const int MAXN=2e5+10;//注意范围 
    const ll INF=1<<30;
    
    struct Splay
    {
        ll sum[MAXN];
        int rt,tot,f[MAXN],sz[MAXN],cnt[MAXN],ch[MAXN][2],val[MAXN];
        void update(int x)
        {
            sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
            sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+1ll*val[x]*cnt[x];
        }
        void rotate(int x)
        {
            int y=f[x],z=f[y],k=(ch[y][1]==x);
            //最好对z特判一下防止对ch[0]操作 
            if(z) ch[z][ch[z][1]==y]=x;f[x]=z;
            ch[y][k]=ch[x][k^1];f[ch[x][k^1]]=y;
            ch[x][k^1]=y;f[y]=x;
            update(y);update(x);
        }
        void splay(int x,int up)
        {
            while(f[x]!=up)
            {
                int y=f[x],z=f[y];
                if(z!=up) (ch[y][0]==x)^(ch[z][0]==y)?rotate(x):rotate(y);
                rotate(x);
            }
            if(!up) rt=x;
            //!!!!!!!!
            update(x);//可能f[x]=up,但也要update 
        }
        void insert(int x)
        {
            int k=rt,anc=0;
            while(k&&val[k]!=x)
                anc=k,k=ch[k][x>val[k]];
            if(k) cnt[k]++;
            else
            {
                k=++tot;
                if(anc) ch[anc][x>val[anc]]=k;
                val[k]=x;cnt[k]=1;f[k]=anc;sz[k]=1;
            }
            splay(k,0);
        }
        void find(int x)
        {
            int k=rt;
            while(x!=val[k]&&ch[k][x>val[k]])
                k=ch[k][x>val[k]];
            splay(k,0);
        }
        int kth(int x)
        {
            int k=rt;
            //对当前为空的情况特判 
            if(sz[k]<x||!x) return 0;
            while(true)
            {
                if(x>sz[ch[k][0]]+cnt[k])
                    x-=sz[ch[k][0]]+cnt[k],k=ch[k][1];
                else if(x<=sz[ch[k][0]]) k=ch[k][0];
                else return k;
            }
        }
        //不用加边界的删除法 
        void del(int x)
        {
            find(x);
            if(val[rt]!=x) return;
            if(cnt[rt]>1) cnt[rt]--;
            else if(!ch[rt][0]||!ch[rt][1])
            {
                int k=ch[rt][0]+ch[rt][1];
                f[k]=0;rt=k;
            }
            else
            {
                int k=ch[rt][0];
                while(ch[k][1]) k=ch[k][1];
                splay(k,rt);
                ch[k][1]=ch[rt][1];
                f[ch[k][1]]=k;
                rt=k;f[k]=0;
            }
            //由于后面没有splay了要update 
            update(rt);
        }
        ll query()
        {
            int k=kth(sz[rt]/2);
            if(!k) return 0;splay(k,0);
            ll ret1=1ll*val[k]*sz[ch[k][0]]-sum[ch[k][0]];
            ll ret2=sum[ch[k][1]]-1ll*val[k]*sz[ch[k][1]];
            return ret1+ret2;
        }
    }s1,s2;
    
    char a[10],b[10];
    ll res=1ll<<60,pre;
    int n,x,y,k,tot;P dat[MAXN];
    
    bool cmp(P a,P b){return a.X+a.Y<b.X+b.Y;};
    
    int main()
    {
        scanf("%d%d",&k,&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%s%d%s%d",a,&x,b,&y);
            if(a[0]==b[0]) pre+=abs(x-y);
            else dat[++tot]=P(x,y),pre++;
        }
        //注意特判!!!
        if(!tot) return printf("%lld",pre),0;
        sort(dat+1,dat+tot+1,cmp);
        
        for(int i=1;i<=tot;i++)
            s1.insert(dat[i].X),s1.insert(dat[i].Y);
        if(k==1) return printf("%lld",s1.query()+pre),0;
        
        for(int i=1;i<=tot;i++)
        {
            s1.del(dat[i].X);s1.del(dat[i].Y);
            s2.insert(dat[i].X);s2.insert(dat[i].Y);
            res=min(res,s1.query()+s2.query());
        }
        printf("%lld",res+pre);
        return 0;
    }
  • 相关阅读:
    linux最常用命令记录(一)
    2020centos解决“nginx 403 Forbidden"错误的故事
    nginx显示静态html爆502 bad gateway的错误提示
    codeigniter框架的使用感受和注意事项
    网闸
    抗DDOS防火墙
    负载均衡
    上网行为管理
    漏洞扫描系统
    网络分析系统
  • 原文地址:https://www.cnblogs.com/newera/p/9717687.html
Copyright © 2011-2022 走看看