CodeForces 283 E.Cow Tennis Tournament(线段树+组合数学)

190 篇文章 1 订阅
59 篇文章 0 订阅

Description

给出 n 个人的能力si,谁能力强谁赢,做 k 次操作,一个操作给出一个区间[l,r],所有能力介于这个区间的两个人输赢情况反转,问 k 次操作后有多少三元组(a,b,c)使得 a b b c c a,两个三元组视为不同当且仅当其所含元素不同

Input

第一行两个整数 n,k ,之后输入 n 个整数si表示第 i 个人的能力,之后k行每行两个整数 l,r 表示一次操作 (3n105,0k105,1si109,1l<r109)

Output

输出 k 次操作后满足条件的三元组数量

Sample Input

3 2
1 2 3
1 2
2 3

Sample Output

1

Solution

所有三元组C3n个,不合法的情况就是一个人赢了其他两个人,故只要对于每个人求出能力比其差的即可,能力比其差有两种——原本就比其差且被操作偶数次的,原本比其厉害但是被操作奇数次的,用线段树维护对于当前人,一个区间中被操作奇数次人的数量,先把人按能力从小到大排序,然后一个个拿出来统计比起能力差的人,对于第 i 个人,先把所有以i为左端点的查询在线段树中实现反转操作,然后查询区间 [0,i1] 中被操作奇数次人的数量 x ix即为区间 [0,i1] 中被操作偶数次的人数量,再查询 [i+1,n1] 中被操作奇数次人的数量 y ,那么ix+y就是比第 i 个人能力差的人数,统计之后,再把所有以i为右端点的查询在线段树中实现反转操作,因为这些比后面人能力差的人即使反转还是差,所以要恢复这些操作造成的影响

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=100005;
#define ls (t<<1)
#define rs ((t<<1)|1)
int num[maxn<<2],lazy[maxn<<2];
void push_up(int t)
{
    num[t]=num[ls]+num[rs];
}
void push_down(int t,int l,int r)
{
    if(lazy[t])
    {
        lazy[t]^=1;
        lazy[ls]^=1,lazy[rs]^=1;
        int mid=(l+r)/2;
        num[ls]=mid-l+1-num[ls],num[rs]=r-mid-num[rs];
    }
}
void update(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)
    {
        num[t]=r-l+1-num[t];
        lazy[t]^=1;
        return ;
    }
    push_down(t,l,r);
    int mid=(l+r)/2;
    if(L<=mid)update(L,R,l,mid,ls);
    if(R>mid)update(L,R,mid+1,r,rs);
    push_up(t);
}
int query(int L,int R,int l,int r,int t)
{
    if(L<=l&&r<=R)return num[t];
    push_down(t,l,r);
    int mid=(l+r)/2,ans=0;
    if(L<=mid)ans+=query(L,R,l,mid,ls);
    if(R>mid)ans+=query(L,R,mid+1,r,rs);
    return ans;
}
int n,k,s[maxn];
vector<int>vl[maxn],vr[maxn];
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)scanf("%d",&s[i]);
    sort(s,s+n);
    while(k--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        l=lower_bound(s,s+n,l)-s;
        r=upper_bound(s,s+n,r)-s-1;
        if(l>r)continue;
        vl[l].push_back(r),vr[r].push_back(l);
    }
    ll ans=(ll)n*(n-1)*(n-2)/6;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<vl[i].size();j++)update(i,vl[i][j],0,n-1,1);
        int res=0;
        if(i)res+=i-query(0,i-1,0,n-1,1);
        if(i<n-1)res+=query(i+1,n-1,0,n-1,1);
        ans-=(ll)res*(res-1)/2;
        for(int j=0;j<vr[i].size();j++)update(vr[i][j],i,0,n-1,1);
    }
    printf("%I64d\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值