• 题解 CF283E 【Cow Tennis Tournament】


    题目大意

    给出n个正整数,由大小确定数与数之间的指向关系(大的指向小的),其中大小处在某一区间的数指向关系全部取反,问n个数之间的三元环个数

    正在做学长推荐的数据结构题,莫名看到了这道数学(蒟蒻感觉对这种有数学成分的题相当无力啊),于是本蒟蒻想(kan ti jie)了好久好久,终于受到学长数学+数据结构的真传切了这题(先orz切题的大佬一波)

    具体思路

    first

    不管怎样,(s_i)数值范围很大,而区间取反也是从数值入手,既然如此,不管三七二十一,先把数值离散化一波,用数值大小的排名代替数值,将数值范围化为(1-n),然后把要取反的区间两端做同样的操作。又因为反正是数值区间上的取反,干脆把所有数再排下序,这样基本操作就完成了。

    second

    三元环不好直接求,,终于发现可以 容斥 一波,先求出所有方案:

    [C_n^3 ]

    然后是不符合题意的方案数,本蒟蒻觉得分为两种:  
    
    1. 一群牛之间互相都不可以击败对方(即一堆相等的正整数),这样的一堆数之间没有指向性。这样的话,我们可以用一个数组(cnt_x),来记录数x出现的个数,而明显看得出来,只要在每堆这些相等的数中任意取2个,则方案不合理。综上,该情况的不合理方案为(式子里的(s_i)为离散化处理过的(s_i)数组):

    [sum_{i=s_1}^{s_n}{C_{cnt_{i}}^2} ]

    1. 一头牛很勇,可以搞其他很多头牛(即一个数很nb,可以指(cha)向其他的很多个数)。而一个数x能指向的数有且仅有两种情况(又是两种……) :a.一个数y比x小且未被经过x的区间经过奇数次(即未被经过x的区间给d掉);b.一个数z比x大且被经过x的区间经过奇数次(即被经过x的区间给d掉)。设第一种情况存在可能的y有(res_1)种,第二种情况存在可能的z有(res_2)种,那么显然,在(res_1)(res_2)中取出任两个和x搭配都会导致方案不合理。设(res=res_1+res_2),则不合理方案数为: (C_{res}^2)。而总的不合理方案书如下:

    [sum_{i=s_1}^{s_n}{C_{res_i}^2} ]

    third

    由上总结后,求解答案的思路清晰很多了呢(没有的话别喷蒟蒻)。但看看上面我们要求的两个东西,(C_{cnt_i}^2)很好求,但那个很勇的牛的情况就有点%¥&……

    关键是经过x的区间要算、而没经过的不能算进去这个不好处理,莫非一个个数值暴力枚举??这样肯定超时,怎么办呢??

    本蒟蒻脑子转得慢,搞不懂怎么处理,去问学长,学长说要用扫描线,我说我不会,于是学长耐心地跟我讲了扫描线,我认真滴听,然后……没听懂。

    关于学长讲的扫描线,我只记得好像是先按区间左界(sort)一波,再操作一波,再按右界(sort)一波,再操作一波,然后就有答案了……

    但我不会操作啊!!

    算了,按照学长的左界(sort)先yy一波,灵光一闪!从(s_1)开始遍历到(s_n),每循环到一个点(i)就把左界小于(i)的还未取反的区间给进行取反,

    然后把区间左界(l)压入以右界(r)为关键词的一个动态数组((vector)(e[r]),然后再把(e[i-1])里的左界一一取出,再进行一次取反(两次取反便为还原,就相当于把区间给排除了)。

    forth

    具体的区间取反使用线段树实现,这样我们就很愉快地切题了

    时间复杂度(O(L+klogL))(L)为s中不同数值的个数, 感觉应该比(O(n+klogn))的期望要好些

    fifth

    祝大家切题愉快

    sixth

    附上代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define in read()
    #define fur(i,a,b) for(int i=a;i<=b;i++)
    #define xx 110000
    #define int long long
    #define ll long long
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        for(;!isalnum(ch);ch=getchar()) if(ch=='-') f=-1;
        for(;isalnum(ch);ch=getchar()) x=x*10+ch-'0';
        return x*f;
    }
    int s2[xx],s1[xx],s[xx],cnt[xx];    //s存战斗力,s2存排序后的,s1存s2去重后的,再将s1里的排名赋到s上
    int n,c[4][xx],all=0;  //c存组合数(当然也可以在线算),all记录s2长度
    struct prom{int a,b;}t[xx];  //存储区间
    struct linetree{int l,r,tot,sum;bool tag;}lt[xx<<2];  //tot存储总数,sum存储被取反数
    inline bool cmp(prom x,prom y){return x.a<y.a;}
    vector<int>e[xx];
    inline int look2(int x)  //取出s1中第一个小于等于x的数的下标,即排名
    {
        int head=1,tail=all,ans=1;
        while(head<=tail)
        {
            int mid=(head+tail)>>1;
            if(s1[mid]<=x) ans=mid,head=mid+1;
            else tail=mid-1;
        }
        return ans;
    }
    inline int look1(int x)  //取出s1中第一个大于等于x的数
    {
        int head=1,tail=all,ans=all;
        while(head<=tail)
        {
            int mid=(head+tail)>>1;
            if(s1[mid]>=x) ans=mid,tail=mid-1;
            else head=mid+1;
        }
        return ans;
    }
    
    //------------
    
    //线段树
    inline void up(int k)
    {
        int q=k<<1;
        lt[k].sum=lt[q].sum+lt[q+1].sum;
        lt[k].tot=lt[q].tot+lt[q+1].tot;
    }
    inline void down(int k)
    {
        if(lt[k].tag)
        {
            int q=k<<1;
            lt[q].sum=lt[q].tot-lt[q].sum;
            lt[q+1].sum=lt[q+1].tot-lt[q+1].sum;
            lt[q].tag^=1;
            lt[q+1].tag^=1;
            lt[k].tag^=1;
        }
    }
    inline void build(int k,int i,int j)
    {
        lt[k].l=i;
        lt[k].r=j;
        if(i==j)
        {
            lt[k].tot=cnt[i];
            return;
        }
        int q=k<<1,mid=(i+j)>>1;
        build(q,i,mid);
        build(q+1,mid+1,j);
        up(k);
    }
    inline void change(int k,int i,int j)
    {
        if(i<=lt[k].l&&j>=lt[k].r)
        {
            lt[k].sum=lt[k].tot-lt[k].sum;
            lt[k].tag^=1;
            return;
        }
        down(k);
        int q=k<<1,mid=lt[q].r;
        if(i<=mid) change(q,i,j);
        if(j>mid) change(q+1,i,j);
        up(k);
    }
    inline int askus(int k,int i,int j) //访问区间中没有被取反的数的个数
    {
    	if(i>j) return 0;
        if(i<=lt[k].l&&j>=lt[k].r) return lt[k].tot-lt[k].sum;
        down(k);
        int q=k<<1,mid=lt[q].r,res=0;
        if(i<=mid) res+=askus(q,i,j);
        if(j>mid) res+=askus(q+1,i,j);
        return res;
    }
    inline int askrs(int k,int i,int j) //访问区间中被取反的数的个数
    {
    	if(i>j) return 0;
        if(i<=lt[k].l&&j>=lt[k].r) return lt[k].sum;
        down(k);
        int q=k<<1,mid=lt[q].r,res=0;
        if(i<=mid) res+=askrs(q,i,j);
        if(j>mid) res+=askrs(q+1,i,j);
        return res;
    }
    //线段树
    
    //------------
    
    signed main()
    {
        n=in;
        fur(i,1,n)
        {
            c[0][i]=1;c[1][i]=i;
            fur(j,2,min(i,3ll)) c[j][i]=c[j-1][i-1]+c[j][i-1];
        }
        int k=in;
        fur(i,1,n) s2[i]=s1[i]=s[i]=in;
        sort(s2+1,s2+n+1);
        fur(i,1,n)
        {
            while(s2[i]==s2[i+1]) i++;
            s1[++all]=s2[i];
        }
        fur(i,1,n) s[i]=look1(s[i]),cnt[s[i]]++;
        sort(s+1,s+n+1);
        build(1,1,s[n]);
        fur(i,1,k) t[i].a=in,t[i].b=in;
        fur(i,1,k) t[i].a=look1(t[i].a),t[i].b=look2(t[i].b);
        sort(t+1,t+k+1,cmp);
        int ans=c[3][n],tnt=1;
        fur(i,1,s[n])
        {
            while(t[tnt].a<=i&&tnt<=k)  //先算区间左界小于等于当前i的
            {
                change(1,t[tnt].a,t[tnt].b);
                e[t[tnt].b].push_back(t[tnt].a);
                tnt++;
            }
            fur(j,0,(int)e[i-1].size()-1) change(1,e[i-1][j],i-1ll);  //再删区间右界小于当前i的
            int res=0;
            res+=askus(1,1,i-1);
            res+=askrs(1,i+1,s[n]);
            ans-=cnt[i]*c[2][res];
    		ans-=c[2][cnt[i]];
    	}
        printf("%lld
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    【训练】9.13 训练赛
    【训练】9.12 训练赛
    【题录】CF#666 Div.2
    【题解】JSOI2009球队收益 / 球队预算
    【申明】——暂别博客园——
    【题解】CF#896 D-Nephren Runs a Cinema
    【题解】洛谷P4707重返现世
    [HNOI2012][BZOJ2732] 射箭 [二分+半平面交]
    平面几何-学习笔记
    [NOI.AC省选模拟赛3.31] 星辰大海 [半平面交]
  • 原文地址:https://www.cnblogs.com/ALANALLEN21LOVE28/p/11312955.html
Copyright © 2020-2023  润新知