【题解】「雅礼集训 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,0−cy,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]));
}
}

浙公网安备 33010602011771号