[CF1548E] Gregor and the Two Painters
题目大意
给定一个 的图和长度为 的序列 ,将 满足 的点设为黑色,求黑色连通块数量。
数据范围满足,。
思路
将所有的 按照 三个元素依次关键字进行排序,记排序后的位置为 。
如果满足 使得 且 在同一个连通块内,那么记 为关键点。
容易发现一个连通块中有且仅有 个关键点,所以答案就是关键点的数量。证明很简单,如果 同时在一个连通块中必然有 ,与定义相矛盾。
定义 表示 , 表示 , 同理。
先考虑一个性质:如果从 可以走到 这一行那么一定可以走到 。
如果可以到达,那么行进的方法一定是先在第 行移动,找到可以到达的 最小的点 。接着移动到 再移动到 。
这样的行动一定是最优的,证明如下:
- 如果在向 纵向移动的过程中还需要横向移动才能到达 ,那么肯定满足 。那么如果可以通过就说明 ,所以有 ,那么 显然是可以到达且 值更小的,与假设相矛盾。
- 如果 无有到达 ,那么因为从 可以到达 ,所以必然有 ,这与定义相矛盾。
容易发现如果 是关键点,那么一定满足 ,因为如果 不是在第 行可以到达的 值最小的,那么一定满足 ,所以 显然更优秀与假设相矛盾。
如果 可以到达 ,那么 显然不是关键点。因为在共用相同的 的情况下,必然满足 大于 ,所以 定然不是关键点。
所以如果 是关键点就需要满足 。
同样的,对于其他的 个方向也需要满足类似要求就可以了。
综上所述,如果 满足以下条件,那么 就是关键点:
不妨设 ,那么可以通过移项可以得到:
提供一种不需要使用线段树的做法:维护一个树状数组做二维偏序。
通过排序可以使第 项满足,考虑怎么满足第一项。如果遇到 那么在这个位置增加 ,否则答案增加 中数的和。
AC Code
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<stack>
using namespace std;
const int N=2e5+5,inf=0x3f3f3f3f;
int n,m,a[N],b[N],lg[N],X,c[N],d[N],sck[N];
struct node{int x,y,op;};
vector<node>g;
stack<int>s;
struct st_max{
int f[N][21],n;
void init(int nn,int a[]){
n=nn;
for(int i=1;i<=n;i++) f[i][0]=a[i];
for (int j=1;j<=lg[n];j++){
for (int i=1;i<=n-(1<<j)+1;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
}
int ask(int x,int y){
if(x>y) return inf;
int l=lg[y-x+1];
return max(f[x][l],f[y-(1<<l)+1][l]);
}
}s1,s2;
struct BIT{
int s[N];
int lowbit(int x){return x&-x;}
void updata(int x,int v=1){
for(int i=x;i<N;i+=lowbit(i)) s[i]+=v;
}
int sum(int x){
int ans=0;
for(int i=x;i>=1;i-=lowbit(i)) ans+=s[i];
return ans;
}
}tree;
bool cmp(node x,node y){
int s1=x.y,s2=y.y;
if(!x.op) s1=X-x.x+1;
if(!y.op) s2=X-y.x+1;
return (s1>s2)||((s1==s2)&&(x.op>y.op));
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>m>>X;
lg[1]=0;
for(int i=2;i<N;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
s1.init(n,a),s2.init(m,b);
memset(c,0x3f,sizeof(c));
memset(d,0x3f,sizeof(d));
for (int i=1;i<=n;i++){
while(!s.empty()&&a[s.top()]>=a[i]) s.pop();
if (!s.empty()) c[i]=min(c[i],s1.ask(s.top()+1,i));
s.push(i);
}
while(!s.empty()) s.pop();
for (int i=n;i>=1;i--){
while(!s.empty()&&a[s.top()]>a[i]) s.pop();
if(!s.empty()) c[i]=min(c[i],s1.ask(i,s.top()-1));
s.push(i);
}
while(!s.empty()) s.pop();
for(int i=1;i<=m;i++){
while(!s.empty()&&b[s.top()]>=b[i]) s.pop();
if (!s.empty()) d[i]=min(d[i],s2.ask(s.top()+1,i));
s.push(i);
}
while(!s.empty()) s.pop();
for(int i=m;i>=1;i--){
while(!s.empty()&&b[s.top()]>b[i]) s.pop();
if (!s.empty()) d[i]=min(d[i],s2.ask(i,s.top()-1));
s.push(i);
}
for(int i=1;i<=n;i++) g.push_back({a[i],c[i],0});
for(int i=1;i<=m;i++) g.push_back({b[i],d[i],1});
sort(g.begin(),g.end(),cmp);
long long ans=0;
for(node i:g){
if(!i.op) ans+=tree.sum(X-i.x)-tree.sum(X-i.y);
else tree.updata(i.x);
}
cout<<ans<<'\n';
return 0;
}