[Luogu3527][POI2011]MET-Meteors

BZOJ权限题!提供洛谷链接!

sol

昨晚突发奇想去学了一下整体二分。
这道题应该算是整体二分的板子题吧。

整体二分

首先要求可离线,不然还是安心码数据结构吧。
整体二分简单的来讲,就是时间复杂度可以承受一次二分答案却无法承受每个询问都二分答案时(比如说依次二分的复杂度是\(O(n\log{n})\),那么\(Q\)次二分答案就是\(O(Qn\log{n})\)),我们把所有询问一起二分答案。往往会使用一些诸如树状数组之类的辅助工具。

拿这道题来讲

“每个国家最早多少次之后收集到指定数量的陨石?”
那么就二分一个时间\(mid\),然后判断前\(mid\)次之内是否达到了指定数量。
我们把所有国家放在一起做,每次二分出了\(mid\)之后,模拟\(l\)\(mid\)这一段的陨石(当然是用树状数组统计啦),然后按照“是否已经达到指定数量”把所有国家分成两份。已经完成了的国家向左递归(因为答案可能会更小),没有完成的国家向右递归,同时指定数量\(p\)中减掉已收集的陨石数量即可。

code

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 300005;
#define ll long long
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
int n,m,k,p[N],st[N],ed[N],val[N],ans[N];
struct nation{ll p;int id;}q[N],q1[N],q2[N];
vector<int>a[N];
ll c[N];
void modify(int k,int v){while (k<=m) c[k]+=v,k+=k&-k;}
ll query(int k){ll s=0;while (k) s+=c[k],k-=k&-k;return s;}
void update(int p,int f)
{
    if (st[p]<=ed[p])
        modify(st[p],val[p]*f),modify(ed[p]+1,-val[p]*f);
    else
        modify(1,val[p]*f),modify(ed[p]+1,-val[p]*f),modify(st[p],val[p]*f);
}
void solve(int L,int R,int l,int r)//[L,R]是国家区间 [l,r]是二分的答案区间
{
    if (L>R) return;
    if (l==r)
    {
        for (int i=L;i<=R;i++)
            ans[q[i].id]=l;
        return;
    }
    int mid=l+r>>1;
    int t1=0,t2=0;ll temp;
    for (int i=l;i<=mid;i++) update(i,1);
    for (int i=L;i<=R;i++)
    {
        temp=0;
        for (int j=0,sz=a[q[i].id].size();j<sz;j++)
            temp+=query(a[q[i].id][j]);
        if (temp>=q[i].p) q1[++t1]=q[i];
        else q[i].p-=temp,q2[++t2]=q[i];
    }
    for (int i=l;i<=mid;i++) update(i,-1);
    for (int i=L,j=1;j<=t1;i++,j++) q[i]=q1[j];
    for (int i=L+t1,j=1;j<=t2;i++,j++) q[i]=q2[j];
    solve(L,L+t1-1,l,mid);solve(L+t1,R,mid+1,r);
}
int main()
{
    n=gi();m=gi();
    for (int i=1;i<=m;i++) a[gi()].push_back(i);
    for (int i=1;i<=n;i++) q[i]=(nation){gi(),i};
    k=gi();
    for (int i=1;i<=k;i++) st[i]=gi(),ed[i]=gi(),val[i]=gi();
    ++k;st[k]=1;ed[k]=m;val[k]=1e9;//设置右哨兵保证所有国家都可以完成
    solve(1,n,1,k);
    for (int i=1;i<=n;i++)
        if (ans[i]==k) puts("NIE");
        else printf("%d\n",ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/zhoushuyu/p/8365216.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值