fjwc2019 D2T2 定价 (栈+set+贪心)
先瞄下数据范围
对于所有数据,1≤n≤1000,1≤m≤10^9,1≤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; }