【题解】「雅礼集训 2017 Day2」水箱

这道题的思路非常巧妙。

怎么说呢,本题最难的地方就在于建模。首先我们按隔板从小到大排序,然后对于每个隔板,我们把它所连接的左右两个区间合成一个区间,这样对于每一个隔板,我们只考虑它的管辖范围内的条件。

在这里插入图片描述

此时不难想到树形dp。设 d p x , 0 dp_{x,0} dpx,0 表示以 x x x 为根的子树,不全部覆盖 的最大答案。 d p x , 1 dp_{x,1} dpx,1 表示以 x x x 为根的子树,全部覆盖 所能满足的条件个数。

第二个问题在于如何找到条件所在的区间。第一个想法是树上倍增,找到最后一个高度小于等于它的隔板,然后放进这个隔板所对应的区间。但是我们忽略了一个问题,就是 d p x , 0 dp_{x,0} dpx,0 的最优转移有可能是 只淹了一半 ,也就是说一半只有 k = 1 k=1 k=1 才有贡献,一半只有 k = 0 k=0 k=0 才有贡献。同时,这个做法并不好实现

考虑第二种做法,也就是说,将条件也按照 y y y 从小到大排序。处理一个隔板时,把高度小于当前隔板高度的条件放进当前区间里,并维护 c x , 1 / 2 c_{x,1/2} cx,1/2 表示 0 / 1 0/1 0/1 的个数, c x , 0 c_{x,0} cx,0 表示淹一半时的最大答案。不难得到 d p dp dp 转移式:

d p x , 0 = ∑ m a x ( d p y , 0 + c y , 2 , d p y , 1 + c y , 0 − c y , 1 ) dp_{x,0}=\sum{max(dp_{y,0}+c_{y,2},dp_{y,1}+c_{y,0}-c_{y,1})} dpx,0=max(dpy,0+cy,2,dpy,1+cy,0cy,1)

d p x , 1 = ∑ d p y , 1 + c x , 1 dp_{x,1}=\sum{dp_{y,1}}+c_{x,1} dpx,1=dpy,1+cx,1

注意我们是先做出了这道题,然后才写出系统的 d p dp dp 转移式的。

考虑 h a c k hack hack:当 y y y 的高度相同时,应该先处理 k = 0 k=0 k=0 ,再处理 k = 1 k=1 k=1 。因为 k = 0 k=0 k=0 是不能算在全部淹没的状态里的。这一点比较恶心,毕竟我当时也没有注意到这一点,导致只有 90 p t s 90pts 90pts

#include <bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
const int mx = 2e5 + 5;
inline int read() {
    int x = 0, f = 1;
    char c = getchar();

    while (c < '0' || c > '9') {
        if (c == '-')
            f = -1;

        c = getchar();
    }

    while (c >= '0' && c <= '9') {
        x = (x << 1) + (x << 3) + c - '0';
        c = getchar();
    }

    return x * f;
}
struct query{
	int x,y,k;
	//hack :90pts
	bool operator <(const query &x) const{
		return y<x.y||y==x.y&&k<x.k;
	}
}q[mx];
int T, n, m, fa[mx], dp[mx][2], c[mx][3], tot;
pii a[mx];
int find(int x) {
	if(fa[x] == -1) return x;
	return fa[x] = find(fa[x]);
}
int main() {
//	freopen("data.in","r",stdin);
	T=read();
	while(T--) {
		n=read(),m=read();memset(dp,0,sizeof(dp)),memset(c,0,sizeof(c)),memset(fa,-1,sizeof(fa));
		for(int i=1;i<n;i++) a[i].fi=read(),a[i].se=i,tot=n;
		sort(a+1,a+n);
		for(int i=1;i<=m;i++) {
			q[i].x=read(),q[i].y=read(),q[i].k=read();
		}
		sort(q+1,q+1+m);
		int j=1;
		for(int i=1;i<n;i++) {
			while(j<=m&&q[j].y<a[i].fi) {
				int id=find(q[j].x);
				if(q[j].k==0) {
					c[id][2]++;
					c[id][0]=max(c[id][0]+1,c[id][1]);
				}
				else {
					c[id][1]++;
					c[id][0]=max(c[id][0],c[id][1]);
				}
				j++;
			}
			int x=find(a[i].se),y=find(a[i].se+1);
			fa[x]=fa[y]=++tot;
			dp[x][1]+=c[x][1];
			dp[y][1]+=c[y][1];
			dp[tot][0]+=max(dp[x][0]+c[x][2],dp[x][1]+c[x][0]-c[x][1])+max(dp[y][0]+c[y][2],dp[y][1]+c[y][0]-c[y][1]);
			dp[tot][1]+=dp[x][1]+dp[y][1];
		}
		while(j<=m) {
			if(q[j].k==0) {
				c[tot][2]++;
				c[tot][0]=max(c[tot][0]+1,c[tot][1]);
			}
			else {
				c[tot][1]++;
				c[tot][0]=max(c[tot][0],c[tot][1]);
			}
			j++;
		}
		printf("%d\n",max(dp[tot][0]+c[tot][2],dp[tot][1]+c[tot][1]));
	}
}
posted @ 2021-06-21 14:51  仰望星空的蚂蚁  阅读(19)  评论(0)    收藏  举报  来源