题解 P6525 【「Wdoi-1」蓬莱玉枝】
题面传送门。
题意简述:从给出的
n 个数中选出若干个数b_1,b_2,\cdots,b_m ,其分值为m\times \max b_i 。求所有满足存在i,j,k 使得b_i,b_j,b_k 能组成一个三角形的方案的分值之和。
题目还是挺不错的,如果模数是质数 / 不卡空间就更棒了。
正着算不方便,考虑用所有方案减去不满足的方案。
首先将给定的数从小到大排序,然后从左往右 DP:设
考虑一个状态能扩展到哪些状态,显然有:
即
初值:
直接三重循环枚举显然不行,接下来是一些优化:
- 每一次内层循环
j 的时候,决策p 是单调的,可以用一个变量维护,均摊复杂度O(n) 。 - 可以发现每次转移的是一段区间(准确来说是
a 的一个后缀),用树状数组前缀和维护即可做到O(1) 转移。
易知所有方案分值之和为:
因为本题模数不是质数(毒瘤),所以需要时空
时间复杂度
int n,tot,sub,a[N],l[N][N],c[N][N],C[N][N];
int main(){
n=read(); for(int i=1;i<=n;i++)a[i]=read(),l[0][i]=c[0][i]=1; sort(a+1,a+n+1);
C[0][0]=1; for(int i=1;i<=n;i++)for(int j=0;j<=i;j++)C[i][j]=!j?1:(C[i-1][j-1]+C[i-1][j])%P;
for(int i=1;i<=n;i++){
int p=i+1; for(int j=0;j<i;j++){
if(j)l[j][i]=(l[j][i-1]+l[j][i])%P,c[j][i]=(c[j][i-1]+c[j][i])%P;
while(p<=n&&a[j]+a[i]>a[p])p++;
if(p<=n)l[i][p]=(l[i][p]+l[j][i]+c[j][i])%P,c[i][p]=(c[i][p]+c[j][i])%P;
sub=(sub+1ll*l[j][i]*a[i])%P;
}
}
for(int i=1;i<=n;i++)for(int j=1;j<=i;j++)tot=(tot+1ll*C[i-1][j-1]*j%P*a[i])%P;
cout<<(tot-sub+P)%P;
return 0;
}