题意:
给定一个环,每个节点有一个所属国家,k次事件,每次对[l,r]区间上的每个点点权加上一个值,求每个国家最早多少次操作之后所有点的点权和能达到一个值。
题解:
整体二分的经典题。
还是考虑二分答案,对询问分治。
这道题关键是怎么判断询问答案是否大于或小于 \(mid\) .
我们只需要看,他一共收集到的能量与 他需要的能量的大小。
如果大的话说明他的答案在 \(mid\) 左边,反之位于 \(mid\) 右边。
这就需要,我们支持一个区间修改,单点查询的数据结构, 树状数组 OR 线段树。
因为树状数组常数比较小,所以我这道题用的是树状数组。
至于 \(l > r\) 的情况,我们可以这么写
chenge(c[i].l,c[i].w); chenge(c[i].r+1,-c[i].w);
if(c[i].l > c[i].r) chenge(1,c[i].w);
这是一种比较神奇的写法(我从学长那里偷学过来的)。不理解的可以自己画一下图。
无解的情况,只需要我们在传参的时候答案的范围是 \(1-m+1\), 如果他的答案是 \(m+1\) 就代表无解
具体Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
const int N = 3e5+10;
vector<int> sta[N];
int n,m,k,x,ans[N],tr[N];
struct node
{
int c,id;
}q[N],stal[N],star[N];
struct modify
{
int l,r,w;
}c[N];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
return s * w;
}
int lowbit(int x){return x & -x;}
void chenge(int x,int val)//树状数组
{
for(; x <= N-5; x += lowbit(x)) tr[x] += val;
}
int ask(int x)
{
int res = 0;
for(; x; x -= lowbit(x)) res += tr[x];
return res;
}
void work(int l,int r,int L,int R)
{
int cntl = 0, cntr = 0, tmp = 0;
if(l == r)
{
if(l != m+1)//他的答案不是无解
{
for(int i = L; i <= R; i++)
{
ans[q[i].id] = l;
}
}
return;
}
int mid = (l+r)>>1;
for(int i = l; i <= mid; i++)//区间修改树状数组
{
chenge(c[i].l,c[i].w); chenge(c[i].r+1,-c[i].w);//记得是 c[i].r + 1,不要写错了
if(c[i].l > c[i].r) chenge(1,c[i].w);
}
for(int i = L; i <= R; i++)
{
tmp = 0;
for(int j = 0; j < sta[q[i].id].size(); j++)//记住不要写成 sta[i].siz 要和询问的编号对应,
//因为他在后面的顺序已经被我们打乱了
{
int x = sta[q[i].id][j];
tmp += ask(x);
if(tmp >= q[i].c) break;//避免爆 long long
}
if(tmp >= q[i].c)
{
stal[++cntl] = q[i];
}
else
{
q[i].c -= tmp;
star[++cntr] = q[i];
}
}
for(int i = l; i <= mid; i++)//还原操作
{
chenge(c[i].l,-c[i].w); chenge(c[i].r+1,c[i].w);
if(c[i].l > c[i].r) chenge(1,-c[i].w);
}
for(int i = L; i <= L + cntl - 1; i++) q[i] = stal[i - L + 1];//给询问重新排个序
for(int i = L + cntl; i <= R; i++) q[i] = star[i - L - cntl + 1];
work(l,mid,L,L+cntl-1); work(mid+1,r,L+cntl,R);//继续二分
}
signed main()
{
n = read(); m = read();
for(int i = 1; i <= m; i++)
{
x = read();
sta[x].push_back(i);//对每个节点的位置存入 Vector
}
for(int i = 1; i <= n; i++)
{
q[i].c = read();
q[i].id = i;
}
m = read();
for(int i = 1; i <= m; i++)
{
c[i].l = read();
c[i].r = read();
c[i].w = read();
}
work(1,m+1,1,n);//整体二分
for(int i = 1; i <= n; i++)
{
if(!ans[i]) puts("NIE");//无解的情况
else printf("%lld\n",ans[i]);
}
return 0;
}
另外此题还有双倍经验 SP10264 METEORS - Meteors
据说 SPoj 的题,洛谷不管。(那是不是可以尽情的水题了,雾)