10.29 cf283E合法三元环+线段树

题意

在这里插入图片描述

思路

  1. pre_work sort+离散化
  2. 如何求合法三元组,考虑补集转化,用C(n,3)-不合法的;
  3. 考虑不合法如何求,这个三元组必定有一个可以战胜其他两个,那么只要统计出他能打败的人,再C(cnt,2),再sum一下
  4. 如何统计他能打败的人(in[i])
  5. 首先,我们考虑一个暴力,O(n*nm);考虑一个矩阵,a [ i ] [ j ] 表示i和j对战的结果,0表示i输,1表示i赢,那么可以只考虑右上方的rt三角形,每次修改只要baoli修改一个RT三角形即可
  6. 瓶颈在于区间修改及查询,可以想到线段树,又发现对于i来说只有覆盖i的修改才对i有用,所以对于所有操作只要扫两遍即可
  7. O(n+mlogn)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll read()
{
	char ch=' ';
	ll f=1;ll x=0;
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
	    x=x*10+ch-'0';ch=getchar();
	}
	return x*f;
}
const ll N=1e5+100;
ll z[N];
struct node
{
	ll l,r;
}c[N];
bool cmp1(node a,node b)
{
	return a.l<b.l;
}
bool cmp2(node a,node b)
{
	return a.r>b.r;
}
struct tree
{
	ll cnt0;
	ll cnt1;
	ll tag; 
}t[N<<2];
ll in[N];

void update(ll rt)
{
	t[rt].cnt0=t[rt<<1].cnt0+t[rt<<1|1].cnt0;
	t[rt].cnt1=t[rt<<1].cnt1+t[rt<<1|1].cnt1;
	return ;
}
void build(ll rt,ll l,ll r)
{
	t[rt].cnt0=1;
	t[rt].cnt1=0;
	t[rt].tag=0;
	if(l==r)
	{
		return ;
	}
	ll mid=(l+r) >> 1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	update(rt);
}
void pushdown(ll rt)
{
	t[rt].tag=0;
	t[rt<<1].tag^=1;
	t[rt<<1|1].tag^=1;
	swap(t[rt<<1].cnt0,t[rt<<1].cnt1);	
	swap(t[rt<<1|1].cnt0,t[rt<<1|1].cnt1);
}
void modify(ll rt,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		swap(t[rt].cnt0,t[rt].cnt1);
		t[rt].tag^=1;
		return ;
	}
	if(t[rt].tag) pushdown(rt); 
	ll mid=(l+r) >> 1;
	if(x<=mid)
	{
		modify(rt<<1,l,mid,x,y);
	}
	if(y>mid)
	{
		modify(rt<<1|1,mid+1,r,x,y);
	}
	update(rt);
}
ll query(ll rt,ll l,ll r,ll x,ll y)
{
	if(l>=x&&r<=y)
	{
		return t[rt].cnt1;
	}
	if(t[rt].tag==1)
	{
		pushdown(rt);
	}
	ll mid=(l+r) >> 1;
	ll ret=0;
	if(x<=mid)
	{
		ret+=query(rt<<1,l,mid,x,y);
	}
	if(y>mid)
	{
		ret+=query(rt<<1|1,mid+1,r,x,y);
	}
	return ret;
}
int main()
{
	ll n,m;
	n=read();m=read();
	ll i,j;
	for(i=1;i<=n;i++)
	{
		z[i]=read();
	}
	sort(z+1,z+1+n);
	for(i=1;i<=m;i++)
	{
		c[i].l=read();
		c[i].r=read();
		c[i].l=lower_bound(z+1,z+1+n,c[i].l)-z;
		c[i].r=upper_bound(z+1,z+1+n,c[i].r)-z-1;
		//cout<<c[i].l<<' '<<c[i].r<<endl;
	}
	sort(c+1,c+1+m,cmp1);
	build(1,1,n);
	for(i=1,j=1;i<n;i++)
	{
		while(c[j].l>=c[j].r&&j<=m) j++;
		while(c[j].l<=i&&j<=m)
		{
			modify(1,1,n,c[j].l,c[j].r);
			j++;
			while(c[j].l>=c[j].r&&j<=m) j++;
		}
		in[i]=query(1,1,n,i+1,n);
	}
	build(1,1,n);
	sort(c+1,c+1+m,cmp2);
	for(i=n,j=1;i>1;i--)
	{
		while(c[j].l>=c[j].r&&j<=m) j++;
		while(c[j].r>=i&&j<=m)
		{
			modify(1,1,n,c[j].l,c[j].r);
			j++;
			while(c[j].l>=c[j].r&&j<=m) j++;
		}
		in[i]+=i-1-query(1,1,n,1,i-1);
	}
	ll ans=n*(n-1)*(n-2)/6;
	for(i=1;i<=n;i++)
	{
		ans-=in[i]*(in[i]-1)/2;
	}
	cout<<ans<<endl;
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值