題意
給定n個擋板和m次回答,每次回答爲x號水池的H+0.5高度是否有水,問這些回答互不矛盾的最大集合。
題解
設定狀態dp[i][0-1]代表第i個區間枚舉到當前回答後有水的最大不矛盾集合和沒水的最大不矛盾集合。1代表有水,0代表沒水。
我們將回答的H從小到大排序後升序枚舉,對於第i個回答,我們可以用線段樹定位出其所影響的區間[l,r]在這個區間內去進行dp轉移。同時我們還發現若枚舉到了一定的高度,我們可以進行區間與區間的合併(由於高度是遞增枚舉的),規定向右合併,這裏用並查集就可以維護。
之後我們就可以寫出轉移(設枚舉到的回答有水爲op=1,沒水爲op=0)
之後將所有l-r的集合合併到r上
最後統計答案的時候只需要將剩餘的集合每個聚合取累加起來即可
代碼
#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;
}