2527: [Poi2011]Meteors 整体二分+树状数组

答案显然是具有二分性的,操作是区间加和单点查询,并且每个国家是独立的,我们考虑整体二分。
每次二分一个mid,将[1,mid]的操作区间都加入答案,然后分治。
容易想到用可持久化线段树继续维护[mid+1,r],避免重复的操作。
然而准备写之前看了PoPoQQQ大爷的题解发现会MLE
然后我就瞬间一脸懵逼。。
然后我们考虑树状数组,并设立一个指针,每次都移动到当前的mid处,并把经过的点加入BIT或从BIT中删除,可以发现每一个点最多会被操作3次,复杂度有了保证。

#include<iostream>
#include<cstdio>
#include<vector>
#define lowbit(i) (i&(-i))
#define ll long long 
#define inf 1000000007
using namespace std;
int n,m,k,now;
ll tree[300005];
struct node {int x,y,z;} p[300005];
struct date {int id,exp;} b[300005],b1[300005],b2[300005];
int ans[300005];
vector <int> a[300005];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void add(int x,int val)
{
    for (int i=x;i<=m;i+=lowbit(i)) tree[i]+=val;
}
inline ll query(int x)
{
    ll tmp=0;
    for (int i=x;i;i-=lowbit(i)) tmp+=tree[i];
    return tmp;
}
inline void change(int x,int f)
{
    if (p[x].x<=p[x].y) add(p[x].x,f*p[x].z),add(p[x].y+1,f*(-p[x].z));
    else add(1,f*p[x].z),add(p[x].y+1,f*(-p[x].z)),add(p[x].x,f*p[x].z);
}
void solve(int t,int w,int l,int r)
{
    if (t>w) return;
    if (l==r)
    {
        for (int i=t;i<=w;i++) ans[b[i].id]=l;
        return;
    }
    int mid=l+r>>1;
    while (now<=mid) change(++now,1);
    while (now>mid) change(now--,-1);
    int p1=0,p2=0;
    for (int i=t;i<=w;i++)
    {
        ll tmp=0;
        for (int j=0;j<a[b[i].id].size();j++)
        {
            tmp+=query(a[b[i].id][j]);
            if (tmp>=b[i].exp) break;
        }
        if (tmp>=b[i].exp) b1[++p1]=b[i]; else b2[++p2]=b[i];
    }
    for (int i=1;i<=p1;i++) b[t+i-1]=b1[i];
    for (int i=1;i<=p2;i++) b[t+i+p1-1]=b2[i];
    solve(t,t+p1-1,l,mid); solve(t+p1,w,mid+1,r);
}   
int main()
{
    n=read(); m=read(); 
    for (int i=1;i<=m;i++) a[read()].push_back(i);
    for (int i=1;i<=n;i++) b[i].exp=read(),b[i].id=i;
    k=read();
    for (int i=1;i<=k;i++)
        p[i].x=read(),p[i].y=read(),p[i].z=read();
    p[++k].x=1; p[k].y=m; p[k].z=inf;
    solve(1,n,1,k);
    for (int i=1;i<=n;i++) 
        ans[i]==k?puts("NIE"):printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值