luogu P3527 [POI2011]MET-Meteors

背景:

最近 CSDN bug \text{CSDN bug} CSDN bug好多。
难道这启示我用博客园备份?
今晚就是用来补博客的坑的。

题目传送门:

https://www.luogu.org/problem/P3527

题意:

给出两个序列 A , b A,b A,b,其中 b i b_i bi表示 b b b序列中下标为 i i i的对应的是 a a a中下标的 b i b_i bi A i A_i Ai表示 a a a的目标状态。
其实 b b b围成了一个环。
有点绕。
然后有区间加操作,有按照顺序的时刻,每一次在 b b b中区间 x x x y y y之间加上 z z z,看作对 b b b对应的 a a a z z z
对于每一个 i i i,询问 a a a中第 i i i个元素什么时刻达到目标状态 A i A_i Ai
自己意会吧。

思路:

考虑整体二分。
二分答案(时刻)。
你发现询问操作只用最后统计出每一个 b b b对应的 a a a中位置的值一次,然后看看是否达到目标。可以用 vector \text{vector} vector确定这种对应关系,将 b b b映射到 a a a上,最后查询即可,容易发现,这样均摊时间复杂度: Θ ( m ) \Theta(m) Θ(m)
修改操作用差分树状数组(当然你可以用线段树)维护,在 b b b中加,将区间断环成链,暴力加即可。
因此时间复杂度: Θ ( m log ⁡ m log ⁡ q ) \Theta(m\log m\log q) Θ(mlogmlogq) q q q为时刻数)。

代码:

听了 brz \text{brz} brz的建议将询问和修改操作用两个东西。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define lowbit(x) ((x)&(-(x)))
#define LL long long
using namespace std;
vector<int> F[300010];
	int n,m,q;
	LL tot[3000010],tr[300010];
	int a[300010],d1[300010],d2[300010],ans[300010];
	struct node{int x,y,z;} b[300010];
void Add(int x,int y)
{
	for(;x<=m;x+=lowbit(x))
		tr[x]+=y;
}
void add(int x,int y,int z)
{
	if(x<=y) Add(x,z),Add(y+1,-z); else Add(1,z),Add(y+1,-z),Add(x,z),Add(m+1,-z);
}
LL getsum(int x)
{
	LL tot=0;
	for(;x;x-=lowbit(x))
		tot+=tr[x];
	return tot;
}
void dfs(int l,int r,int L,int R)
{
	if(l==r)
	{
		for(int i=L;i<=R;i++)
			ans[a[i]]=l;
		return;
	}
	int mid=(l+r)>>1,t1=0,t2=0;
	for(int i=l;i<=mid;i++)
		add(b[i].x,b[i].y,b[i].z);
	for(int i=L;i<=R;i++)
	{
		LL sum=0;
		bool flag=false;
		for(int j=0;j<F[a[i]].size();j++)
		{
			sum+=getsum(F[a[i]][j]);
			if(sum>=tot[a[i]]) {flag=true,d1[++t1]=a[i];break;}
		}
		if(!flag) tot[a[i]]-=sum,d2[++t2]=a[i];
	}
	for(int i=l;i<=mid;i++)
		add(b[i].x,b[i].y,-b[i].z);
	for(int i=1;i<=t1;i++)
		a[L+i-1]=d1[i];
	for(int i=1;i<=t2;i++)
		a[L+t1+i-1]=d2[i];
	dfs(l,mid,L,L+t1-1);
	dfs(mid+1,r,L+t1-1+1,R);
}
int main()
{
	int x,y,z;
	scanf("%d %d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d",&x);
		F[x].push_back(i);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&tot[i]);
		a[i]=i;
	}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		scanf("%d %d %d",&x,&y,&z);
		b[i]=(node){x,y,z};
	}
	dfs(1,q+1,1,n);
	for(int i=1;i<=n;i++)
		if(ans[i]<=q) printf("%d\n",ans[i]); else printf("NIE\n");
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值