题意
给定n个挡板和m次回答,每次回答为x号水池的H+0.5高度是否有水,问这些回答互不矛盾的最大集合。
题解
设定状态dp[i][0-1]代表第i个区间枚举到当前回答后有水的最大不矛盾集合和没水的最大不矛盾集合。1代表有水,0代表没水。
我们将回答的H从小到大排序后升序枚举,对于第i个回答,我们可以用线段树定位出其所影响的区间[l,r]在这个区间内去进行dp转移。同时我们还发现若枚举到了一定的高度,我们可以进行区间与区间的合并(由于高度是递增枚举的),规定向右合并,这里用并查集就可以维护。
之后我们就可以写出转移(设枚举到的回答有水为op=1,没水为op=0)
d
p
[
r
]
[
1
]
=
∑
i
=
l
r
d
p
[
i
]
[
1
]
+
o
p
?
1
:
0
dp[r][1]=\sum_{i=l}^{r}{dp[i][1]}+op?1:0
dp[r][1]=∑i=lrdp[i][1]+op?1:0
d
p
[
r
]
[
0
]
=
∑
i
=
l
r
m
a
x
(
d
p
[
i
]
[
0
]
,
d
p
[
i
]
[
1
]
)
+
o
p
?
0
:
1
dp[r][0]=\sum_{i=l}^{r}{max(dp[i][0],dp[i][1])}+op?0:1
dp[r][0]=∑i=lrmax(dp[i][0],dp[i][1])+op?0:1
之后将所有l-r的集合合并到r上
最后统计答案的时候只需要将剩余的集合每个聚合取
m
a
x
(
d
p
[
i
]
[
0
]
,
d
p
[
i
]
[
1
]
)
max(dp[i][0],dp[i][1])
max(dp[i][0],dp[i][1])累加起来即可
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
#include <cmath>
#include <vector>
#include <map>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
#define PB push_back
#define MP make_pair
#define Fi first
#define Se second
const int N=3e5+7,M=2e6+7;
struct node{
int x,H,op;
}que[N];
int fa[N],h[N];
int n,m;
bool cmp(node a,node b){
return a.H<b.H;
}
int cha(int x){
if(fa[x]==x) return x;
return fa[x]=cha(fa[x]);
}
int Max(int a,int b){return a > b?a:b;}
namespace sgt
{
#define mid ((l+r)>>1)
int maxx[N<<2];
void up(int rt)
{
maxx[rt] = Max(maxx[rt<<1],maxx[rt<<1|1]);
}
void build(int rt,int l,int r)
{
if(l==r)
{
maxx[rt] = h[l];
return;
}
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
up(rt);
}
int query_l(int rt,int l,int r,int x,int y,int v)
{
if(maxx[rt]<=v) return -1;
if(l==r)
{
return l;
}
int ans = -1;
if(y>mid) ans = query_l(rt<<1|1,mid+1,r,x,y,v);
if(ans==-1&&x<=mid) ans = query_l(rt<<1,l,mid,x,y,v);
return ans;
}
int query_r(int rt,int l,int r,int x,int y,int v)
{
if(maxx[rt]<=v) return -1;
if(l==r) return l;
int ans = -1;
if(x<=mid) ans =query_r(rt<<1,l,mid,x,y,v);
if(ans==-1&&mid<y) ans = query_r(rt<<1|1,mid+1,r,x,y,v);
return ans;
}
#undef mid
}
int get(int x,int v,int flag)
{
if(flag==0)
{
if(x==1) return -1;
int xb = sgt::query_l(1,1,n,1,x-1,v);
return xb;
}
else
{
int xb = sgt::query_r(1,1,n,x,n,v);
return xb;
}
}
int dp[N][3];
int main(){
//freopen("d.txt","r",stdin);
int t;
int ti=0;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n-1;i++){
scanf("%d",&h[i]);
}
h[n]=1e9+7;
sgt::build(1,1,n);
for(int i=1;i<=n+3;i++) fa[i]=i;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&que[i].x,&que[i].H,&que[i].op);
}
sort(que+1,que+1+m,cmp);
for(int i=1;i<=m;i++){
int now=que[i].x;
int l=get(que[i].x,que[i].H,0);
int r=get(que[i].x,que[i].H,1);
if(l==-1) l=1;
else l++;
//cout<<l<<' '<<r<<' '<<que[i].H<<endl;
int x=cha(l);
int y=cha(r);
//cout<<x<<' '<<y<<' '<<que
if(x==y){
int res1=0,res0=0;
if(que[i].op==1){
res0=max(dp[y][1],dp[y][0]);
res1=dp[y][1]+1;
}
else {
res0=max(dp[y][1],dp[y][0])+1;
res1=dp[y][1];
}
dp[y][1]=res1;
dp[y][0]=res0;
}
else {
int res1=0,res0=0;
while(x!=y){
if(que[i].op==1){
res0+=max(dp[x][1],dp[x][0]);
res1+=dp[x][1];
}
else {
res0+=max(dp[x][1],dp[x][0]);
res1+=dp[x][1];
}
fa[x]=cha(x+1);
x=cha(x);
}
if(que[i].op==1){
res0+=max(dp[y][1],dp[y][0]);
res1+=dp[y][1]+1;
}
else {
res0+=max(dp[y][1],dp[y][0])+1;
res1+=dp[y][1];
}
dp[y][1]=res1;
dp[y][0]=res0;
}
//cout<<"-----"<<que[i].H<<' '<<que[i].op<<endl;
//cout<<dp[y][1]<<' '<<dp[y][0]<<endl;
}
int x=cha(1);
int y=cha(n);
int ans=0;
while(x!=y){
ans+=max(dp[x][0],dp[x][1]);
//ans+=dp[x][1];
x=cha(x+1);
}
ans+=max(dp[y][1],dp[y][0]);
//ans+=dp[y][1];
printf("Case #%d: %d\n",++ti,ans);
}
return 0;
}