max67
2022-03-30 11:59:56
题解写到一半时突然 wmh 跑过来把我题解删了。 阿巴阿巴阿巴......
(pass:用 [i] 表示引理 ctrl+f
。)
我们把题目中的
首先考虑一个基础的贪心:对于一次询问中的
对于两个数
i 和j (L_i,L_j\le k_i ,R_i\le R_j )来说,对于\forall l \in (i,n] 来说,都有L_i,L_j \le k_i \le k_l ,即这两个数在考虑k_l 的贡献时只需要考虑R 的限制。那么显然是把R 大的留在后面更优。
但暴力模拟贪心会炸(
观察贪心过程,我们会发现他有一个优美的性质:只有一次操作时,对于所有满足的
由于这个优美的性质,若有第二次操作时,我们只需要考虑两个部分
知道了能取的数的集合,那么我们就要讨论剩下的数(考虑
(红线标的部分为
那么就是先从下到上取到第二个集合直到红线部分,再按
转化一下判断方法,即当我们剩余的数的名额从前往后贪心的取,(就相当于贪心地从后往前取对
基此,我们发现了一个妙妙的方法:([2])若按上面的判断方法能越过红线的话,
因此,若我们按照上面的方法合并的话,我们发现每个区间剩余的数的右端点的最小高度单调递减。
我们睿智地看了一眼,发现这和单调栈非常像。因此我们用单调栈进行操作:
(若单调栈中没有数,默认为
记
对于一个新的元素
然后对于栈中的每个元素(设为
容易发现,上面所说的数据结构就是主席树,可以满足:
求
求出
注意,由于可能没有数可取,这时定义
由于数据范围很小,所以不用离散化。
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
using namespace std;
const int N=1e7+1e3;
struct Segment_Tree
{
int tot;
int rt[N],ls[N],rs[N];
int val[N];
#define midd ((l+r)>>1)
int New(){return ++tot;}
void insert(int &x,int y,int l,int r,int p)
{
x=New();val[x]=val[y]+1;
ls[x]=ls[y];rs[x]=rs[y];
if(l==r)return;
int mid=midd;
if(p<=mid)insert(ls[x],ls[y],l,mid,p);
else insert(rs[x],rs[y],mid+1,r,p);
}
int qpos(int x,int y,int l,int r,int k)
// 求出从大到小的第 i 位的 值
{
if(l==r)return l;
int t=val[rs[x]]-val[rs[y]],mid=midd;
if(t>=k)return qpos(rs[x],rs[y],mid+1,r,k);
return qpos(ls[x],ls[y],l,mid,k-t);
}
int query(int x,int y,int l,int r,int p)
//求出大于等于 p 的数量
{
if(!x)return 0;
if(l==r)return val[x]-val[y];
int mid=midd;
if(p<=mid)return query(ls[x],ls[y],l,mid,p)+val[rs[x]]-val[rs[y]];
return query(rs[x],rs[y],mid+1,r,p);
}
#undef midd
}T;
int n,m;
int k[N];
pii a[N];
stack<int>st;
int hater[N],s[N];
void work()
{
stack<int>st;while(!st.empty())st.pop();
for(int i=1;i<=m;i++)
{
while(!st.empty()&&hater[st.top()]<k[i])st.pop();
int pre=(st.empty()?0:st.top());
int sum=s[pre]+T.query(T.rt[k[i]],T.rt[k[pre]],1,n+1,k[i])-k[i];
if(sum<0)return puts("0"),void();
int h=T.qpos(T.rt[k[i]],T.rt[k[pre]],1,n+1,sum-s[pre]);
while(!st.empty()&&h>=hater[st.top()])
{
st.pop();pre=(st.empty()?0:st.top());
h=T.qpos(T.rt[k[i]],T.rt[k[pre]],1,n+1,sum-s[pre]);
}
st.push(i);hater[i]=h;s[i]=sum;
}
puts("1");
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i].fi,&a[i].se);
sort(a+1,a+n+1);
for(int i=1,j=1;i<=n+1;i++)
//注意 n+1
{
T.rt[i]=T.rt[i-1];
while(j<=n&&a[j].fi==i)T.insert(T.rt[i],T.rt[i],1,n+1,a[j].se),j++;
}
int _;scanf("%d",&_);
while(_--)
{
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d",&k[i]);
sort(k+1,k+m+1);
work();
}
return 0;
}
这题希望评个黑(这样我就有黑题题解了)。
感谢人 win legendgod 大佬 的指导 。