fjwc2019 D2T2 定价 (栈+set+贪心)

#182. 「2019冬令营提高组」定价

先瞄下数据范围

对于所有数据,1n1000,1≤m≤10^91≤q≤500000 。\textbf{2 操作的个数不超过 1000。}

$10^9$位,看起来挺吓人,咋维护每个可以为1的位鸭?

再仔细看看,$q<=500000$,最多500000次操作,那么显然是对每一列,开个动态开点线段树或者平衡树维护。

本题对数据结构要求不高,于是我们可以快捷地用set代替辣

接下来我们考虑如何求出价格和的最小值

2 操作的个数不超过 1000。

这告诉我们可以愉快地在O(nlogn)内解决

从左到右一列列扫过去........考虑贪心

切一段ppt

我们先总结一下贪心的时候在干啥。

假设我们上一行有若干位为1,那么对于下一行,我们需要找到最高的为1位,满足这一行这位不能再为1了,我们就需要选取在这个位置之前的一个可以变成1的0,把它变成1,并把后面的位置全变成0。

直接用bitset维护可以获得部分分。

我们考虑使用一个栈维护当前的1,假设我们能找到最高的不能继续为1的位,我们就可以从这一位开始依次遍历前面的1,找到这个1之前第一个可以变成1的位置,如果这个位置在下一个1之前就是答案。

举个栗子

设前面$i$列处理完后,已知第$i$列的最小值$=(10101101)_{2}$

而第$i+1$列允许为$1$的位(从左到右,从$1$开始)有第$1,2,3,4,5,6$位

加入没有限制,那么第$i+1$列的最优解$=(10101110)_{2}$

但是允许为$1$的位不包括第$7$位鸭

于是我们就只能再向左找,找到第$4$位

把第$4$位改为$1$,并将右边所有位改为$0$

最后第$i+1$列的最优解$=(10110000)_{2}$

即为:

$a[i]=(10101101)_{2}$

$a[i+1]=(10110000)_{2}$

然后顺便维护下答案就ok了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<set>

namespace IO {
    const int lim = (1 << 20) + 500;

    char buf[lim + 5], *S, *T;

    inline char gc() {
        if (S == T) {
            T = (S = buf) + fread(buf, 1, lim, stdin);
            if (S == T) return EOF;
        }
        return *S++;
    }

    inline int read() {
        int x; char c; bool f;
        for (f = 0; (c = gc()) < '0' || c > '9'; f = c == '-');
        for (x = c ^ '0'; (c = gc()) >= '0' && c <= '9'; x = (x << 1) + (x << 3) + (c ^ '0'));
        return f ? -x : x;
    }
}
using namespace IO;//快读是题目给的(逃

using namespace std;
const int mod=1e9+7;
int n,m,q,stk[500005],v[500005],tp;
set <int> s[1005];
set <int>::iterator it;
int Pow(int x,int y){
    int re=1;
    for(;y;y>>=1,x=1ll*x*x%mod)
        if(y&1) re=1ll*re*x%mod;
    return re;
}
int solve(){
    int re=0; tp=0;
    for(int i=1,j;i<=n;++i){
        stk[tp+1]=m+1;
        for(j=1;j<=tp&&s[i].count(stk[j]);++j);//这一位填不了的话就从这位开始找
        tp=j-1;
        for(;;){
            it=s[i].upper_bound(stk[tp+1]-1);
            if(it==s[i].begin()) return -1;
            --it;
            if(*it>stk[tp]){stk[++tp]=*it; break;}
            if(!tp) return -1;
            --tp;
        }
        v[tp]=v[tp-1]+Pow(2,m-stk[tp]);//上个的答案(用数组维护)加上这一位
        if(v[tp]>=mod) v[tp]-=mod;
        re+=v[tp];
        if(re>=mod) re-=mod;
    }return re;
}
int main(){
    freopen("price.in","r",stdin);
    freopen("price.out","w",stdout);
    n=read(); m=read(); q=read();
    int q1,q2;
    while(q--){
        if(read()==1){
            q1=read(); q2=read();
            if(s[q1].count(q2)) s[q1].erase(q2);
            else s[q1].insert(q2);
        }else printf("%d\n",solve());
    }return 0;
}

 

posted @ 2019-02-26 23:46  kafuuchino  阅读(475)  评论(2编辑  收藏  举报