正题
题目链接:https://www.ybtoj.com.cn/contest/68/problem/4
题目大意
mmm个三元组(ai,bi,ci)(a_i,b_i,c_i)(ai,bi,ci),如果ci≥min{xj}(ai≤j≤bi)c_i\geq min\{x_j\}(a_i\leq j\leq b_i)ci≥min{xj}(ai≤j≤bi)那么可以获得min{xj}min\{x_j\}min{xj}的价值,求一个xxx序列使得价值和最大。
解题思路
如果根据xix_ixi构造一个笛卡尔树,那么三元组(ai,bi,ci)(a_i,b_i,c_i)(ai,bi,ci)会在LCA(ai,bi)LCA(a_i,b_i)LCA(ai,bi)处产生一次贡献,考虑用区间dpdpdp构造笛卡尔树。
显然xix_ixi肯定是某个ccc,所以先把ccc离散化了
设fl,r,if_{l,r,i}fl,r,i表示已经构造出了[l,r][l,r][l,r]这个范围的笛卡尔树,然后目前的根节点权值是iii时的最大价值和。
然后每次做完处理后缀和,就可以O(n3m)O(n^3m)O(n3m)实现转移了。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=55,M=4100;
int n,m,a[M],b[M],c[M],t[M],cnt[M],tot;
int f[N][N][M],g[N][N][M],s[N][N][M],z[N][N][M];
void ct(int l,int r,int k){memset(cnt,0,sizeof(cnt));for(int i=1;i<=m;i++)if(a[i]>=l&&a[i]<=k&&b[i]>=k&&b[i]<=r)cnt[c[i]]++;for(int i=tot;i>=1;i--)cnt[i]+=cnt[i+1];return;
}
void print(int l,int r,int x){if(!l||!r||l>r)return;x=z[l][r][x];print(l,g[l][r][x]-1,x);printf("%d ",t[x]);print(g[l][r][x]+1,r,x);
}
int main()
{freopen("baddream.in","r",stdin);freopen("baddream.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&a[i],&b[i],&c[i]);t[i]=c[i];}sort(t+1,t+1+m);tot=unique(t+1,t+1+m)-t-1;for(int i=1;i<=m;i++)c[i]=lower_bound(t+1,t+1+tot,c[i])-t;ct(1,n,5);for(int len=1;len<=n;len++){for(int l=1;l<=n;l++){int r=l+len-1;for(int k=l;k<=r;k++){ct(l,r,k);for(int i=1;i<=tot;i++){if(s[l][k-1][i]+s[k+1][r][i]+cnt[i]*t[i]>f[l][r][i]||!f[l][r][i])f[l][r][i]=s[l][k-1][i]+s[k+1][r][i]+cnt[i]*t[i],g[l][r][i]=k;}}for(int i=tot;i>=1;i--){if(s[l][r][i+1]>f[l][r][i])s[l][r][i]=s[l][r][i+1],z[l][r][i]=z[l][r][i+1];else s[l][r][i]=f[l][r][i],z[l][r][i]=i;}}}printf("%d\n",s[1][n][1]);print(1,n,1);
}