C1099 [Contest #8] 菜菜种菜 (树状数组+二维偏序)(好题)

题目链接
在这里插入图片描述
在这里插入图片描述
思路:首先转换以下问题,我们可以设置两个数组L【i】和R【i】,L【i】表示比i的左边所能到达的最近的数,R【i】表示比i的右边所能到达的最近的数,于是问题就转化为了,对于一个查询【l,r】如果i存在贡献的话,i一定满足l<=i<=r并且L【i】<l&&R【i】>r,这个就是典型的偏序问题了。我们还是先固定一遍,我们把查询按右端点固定,然后存一个优先队列表示当前查询下合法的i的集合。
对于当前的查询【li,ri】来说,我们先把i小于等于ri点的先全部存队列,我们假设这些点全部都能产生贡献,然后再查看队列里那些元素不符合条件,把不符合的全部pop同时把他们之前update给还原。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+100;
#define pi  pair<int,int>
ll c[maxn],a[maxn],l[maxn],r[maxn],ans=0;
struct cxk{
	int l,r,id;
}s[maxn];
int lowbit(int x) {return x&-x;}
bool cmp(const cxk &a,const cxk &b)
{
	return a.r<b.r;
}
void update(int x,int v)
{
	if(x==0) return ;
	while(x<maxn) c[x]+=v,x+=lowbit(x);
}
ll query(int x)
{
	ll res=0;
	while(x>0) res+=c[x],x-=lowbit(x);
	return res;
}
int main()
{
	int n,m,q,u,v;
	scanf("%d %d %d",&n,&m,&q);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]),l[i]=0,r[i]=1e9;
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&u,&v);
		if(v<u) l[u]=max(l[u],1LL*v);
		else r[u]=min(r[u],1LL*v);
	}
	for(int i=1;i<=q;++i) scanf("%d %d",&s[i].l,&s[i].r),s[i].id=i;
	sort(s+1,s+1+q,cmp);
	int now=0;
	priority_queue<pi, vector<pi>, greater<pi> >qq;
	for(int i=1;i<=q;++i)
	{
		while(now+1<=s[i].r)//先假设全部合法
		{
			qq.push({r[now+1],now+1});
			update(now+1,a[now+1]);
			update(l[now+1],-a[now+1]);
			now++;
		}
		while(!qq.empty()&&qq.top().first<=now)//把队列里不合法的剔除掉,然后把他们之前的更新给还原
		{
			pi x=qq.top();
			qq.pop(); 
			update(x.second,-a[x.second]);
			update(l[x.second],a[x.second]);
		}
		ans^=s[i].id*(query(s[i].r)-query(s[i].l-1));
	}
	printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值