(dp)AT 一些区间函数 dp
给定一些奇怪的区间函数,然后要求满足这个区间函数的区间对数和。这种一般都是 dp,做每个下标作为有效右端点新增的贡献。
1.AT_abc159_f Knapsack for All Segments
题意
给定一个长度为 NNN 的整数序列 A1,A2,…,ANA_1, A_2, \ldots, A_NA1,A2,…,AN 和一个正整数 SSS。
对于所有满足 1≤L≤R≤N1 \leq L \leq R \leq N1≤L≤R≤N 的整数对 (L,R)(L, R)(L,R),定义 f(L,R)f(L, R)f(L,R) 如下:
- f(L,R)f(L, R)f(L,R) 表示满足 L≤x1<x2<⋯<xk≤RL \leq x_1 < x_2 < \cdots < x_k \leq RL≤x1<x2<⋯<xk≤R 且 Ax1+Ax2+⋯+Axk=SA_{x_1} + A_{x_2} + \cdots + A_{x_k} = SAx1+Ax2+⋯+Axk=S 的整数序列 (x1,x2,…,xk)(x_1, x_2, \ldots, x_k)(x1,x2,…,xk) 的个数。
请计算所有满足 1≤L≤R≤N1 \leq L \leq R \leq N1≤L≤R≤N 的整数对 (L,R)(L, R)(L,R) 的 f(L,R)f(L, R)f(L,R) 之和。由于答案可能非常大,请输出其对 998244353998244353998244353 取模的结果。
1≤N≤30001 \leq N \leq 30001≤N≤3000,1≤S≤30001 \leq S \leq 30001≤S≤3000,1≤Ai≤30001 \leq A_i \leq 30001≤Ai≤3000。
思路
注意这里是子序列。
若 Ax1+Ax2+...+Axk=SA_{x_1}+A_{x_2}+...+A_{x_k}=SAx1+Ax2+...+Axk=S,那么这个子序列可以贡献到 f(1∼x1,xk∼n)f(1\sim x_1,x_k\sim n)f(1∼x1,xk∼n)。若确定 i=xki=x_ki=xk,iii 就有大小为 x1x_1x1 的贡献了。
于是设 fsf_sfs 表示和为 sss 的贡献,每次新增强制选择新右端点 iii 上 aia_iai 的贡献:
fj←fj−aif_j\leftarrow f_{j-a_i}fj←fj−ai
同时和为 aia_iai 的也有单选一个的贡献:
fai←if_{a_i}\leftarrow ifai←i
因为到 iii 时会继承 i−1i-1i−1 的贡献,所以 ans←fSans\leftarrow f_Sans←fS 会计算 1∼i−11\sim i-11∼i−1 右端点延伸到 nnn 的贡献。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=3002,mod=998244353;
ll n,s,a[N];
ll f[N];
int main()
{scanf("%lld%lld",&n,&s);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);ll ans=0;f[0]=0;for(int i=1;i<=n;i++){for(int j=s;j>a[i];j--)f[j]=(f[j]+f[j-a[i]])%mod;f[a[i]]=(f[a[i]]+i)%mod;ans=(ans+f[s])%mod;}printf("%lld",ans); return 0;
}
2.AT_arc169_b Subsegments with Small Sums
题意
给定一个正整数 SSS。对于正整数序列 xxx ,我们定义函数 f(x)f(x)f(x) 如下:
- 将 xxx 分解为几个连续的子序列。对于每个连续子序列,其元素之和最多为 SSS。f(x)f(x)f(x) 是在这样的要求下分解成的连续子序列的最小数目。
现在给定一个长度为 NNN 的正整数序列 A=(A1,A2,⋯ ,AN)A=(A_1,A_2,\cdots,A_N)A=(A1,A2,⋯,AN),请求出 ∑1≤l≤r≤Nf((Al,Al+1,⋯ ,Ar))\sum_{1 \leq l \leq r \leq N} f((A_l,A_{l+1},\cdots,A_r))∑1≤l≤r≤Nf((Al,Al+1,⋯,Ar))。
1≤N≤2500001 \leq N \leq 2500001≤N≤250000,1≤S≤10151 \leq S \leq 10^{15}1≤S≤1015,1≤Ai≤min(S,109)1 \leq A_i \leq \min(S,10^9)1≤Ai≤min(S,109)。
思路
一个经典的贪心是,尽量合并直到不能合并,然后开一段新区间。从头或从尾开始合并,区间数都相同。
于是考虑枚举右端点 iii,计算 fif_ifi 表示以 iii 为右端点,1∼i1\sim i1∼i 为左端点的分解区间数目之和。
于是用指针预处理出,iii 之前第一个 lasi=llas_i=llasi=l,使得 ∑j=lasiiAj>S\sum\limits_{j=las_i}^iA_j>Sj=lasi∑iAj>S 而 ∑j=lasi+1iAj≤S\sum\limits_{j=las_i+1}^iA_j\le Sj=lasi+1∑iAj≤S,就可以在 flasif_{las_i}flasi 后面,加上 (lasi,i](las_i,i](lasi,i],即:
fi←flasi+if_i\leftarrow f_{las_i}+ifi←flasi+i
答案就是 ∑fi\sum f_i∑fi。具体细节见代码。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2.5e5+9,inf=3e14;
ll n,S,a[N];
ll s[N],las[N],f[N];
int main()
{scanf("%lld%lld",&n,&S);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);s[i]=s[i-1]+a[i];}ll pos=n+1;for(int i=n;i>=1;i--){while(pos>=1&&s[i]-s[pos-1]<=S)pos--;las[i]=pos;}ll ans=0;for(int i=1;i<=n;i++){cout<<i<<" "<<las[i]<<endl;f[i]=f[las[i]]+i;ans+=f[i];}printf("%lld",ans);return 0;
}
3.AT_arc059_c [ARC059E] キャンディーとN人の子供
题意
AtCoder 幼儿园里有 NNN 个小朋友,编号 1∼N1\sim N1∼N,Evi 先生要把 CCC 颗糖果分给他们。
小朋友可以得到任意多颗糖果。如果第 iii 个小朋友得到了 aaa 颗糖,那么他会得到 xiax_i^axia 的愉悦度,其中 xix_ixi 是第 iii 个小朋友的兴奋度。幼儿园活跃指数定义为 NNN 个小朋友愉悦度的乘积。
令 f(x1,x2,⋯ ,xN)f(x_1,x_2,\cdots,x_N)f(x1,x2,⋯,xN) 表示所有分糖果的方案对应的幼儿园活跃指数的和。
现在给出 Ai,Bi(1≤i≤N)A_i,B_i(1\le i\le N)Ai,Bi(1≤i≤N),求 ∑x1=A1B1∑x2=A2B2⋯∑xN=ANBNf(x1,x2,...,xN)\sum\limits_{x_1=A_1}^{B_1} \sum\limits_{x_2=A_2}^{B_2} \cdots \sum\limits_{x_N=A_N}^{B_N} f(x_1,x_2,...,x_N)x1=A1∑B1x2=A2∑B2⋯xN=AN∑BNf(x1,x2,...,xN),对 109+710 ^ 9 + 7109+7 取模。
1≤N,C≤4001 \le N,C \le 4001≤N,C≤400,1≤Ai≤Bi≤4001\le A_i\le B_i \le 4001≤Ai≤Bi≤400。
思路
用背包给每个小朋友分配糖果,设小朋友 iii 分到 kkk 颗糖果,那么贡献就是 ∑x=AiBixk\sum\limits_{x=A_i}^{B_i}x^kx=Ai∑Bixk,可以前缀和优化掉。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=405,mod=1e9+7;
ll n,c;
ll l[N],r[N],s[N][N];
ll f[N][N];
ll qpow(ll x,ll k)
{ll ret=1;while(k){if(k&1)ret=ret*x%mod;x=x*x%mod;k>>=1; }return ret;
}
int main()
{scanf("%lld%lld",&n,&c);for(int i=1;i<=n;i++)scanf("%lld",&l[i]);for(int i=1;i<=n;i++)scanf("%lld",&r[i]);for(int a=0;a<N;a++)for(ll i=1;i<N;i++)s[a][i]=(s[a][i-1]+qpow(i,a))%mod;f[0][0]=1;for(int i=1;i<=n;i++){for(int j=0;j<=c;j++){for(int k=0;k<=j;k++){f[i][j]=(f[i][j]+f[i-1][j-k]*(s[k][r[i]]-s[k][l[i]-1]+mod)%mod)%mod;}}}printf("%lld",f[n][c]);return 0;
}
