定义深度为到根的边权和,考虑按深度
d
p
dp
dp,显然深度小的放的是出现次数较多的数 即放得是一个后缀,记录
d
p
i
,
a
,
b
,
c
dp_{i,a,b,c}
dpi,a,b,c 表示到第
i
i
i 层当前层有
a
a
a 个,下一层可以放
b
b
b 个,选的后缀为
c
c
c 的最小值,枚举多少个后缀放当前层,对应转移到
d
p
i
+
1
,
b
+
(
a
−
t
)
,
a
−
t
,
c
+
t
dp_{i+1,b+(a-t),a-t,c+t}
dpi+1,b+(a−t),a−t,c+t,系数是
i
∗
t
i*t
i∗t 系数可以提前计算,于是可以压掉层数一维,对应转移到
d
p
b
+
a
−
t
,
a
−
t
,
c
+
t
dp_{b+a-t,a-t,c+t}
dpb+a−t,a−t,c+t,系数是
∑
i
=
1
c
a
i
\sum_{i=1}^ca_i
∑i=1cai 考虑将后缀一个一个加入,枚举是当前层还是下一层,到当前层是一个完全背包,复杂度
O
(
n
3
)
O(n^3)
O(n3)
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N =755;int n; ll c[N], dp[2][N][N];
void ckmin(ll &a, ll b){if(a > b) a = b;}intmain(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
scanf("%d",&n);for(int i=1; i<=n; i++)scanf("%lld",&c[i]);sort(c+1,c+n+1);for(int i=1; i<=n; i++) c[i]+=c[i-1];int now=0, nxt=1;memset(dp[now],0x3f,sizeof(dp[now]));
dp[now][1][1]= c[n];for(int i=n; i>=1; i--){memset(dp[nxt],0x3f,sizeof(dp[nxt]));for(int j=0; j<=i; j++)for(int k=0; j+k<=i; k++)ckmin(dp[now][j+k][j],dp[now][j][k]+c[i]);for(int j=1; j<=i; j++)for(int k=0; j+k<=i; k++)ckmin(dp[nxt][j-1][k],dp[now][j][k]);swap(now,nxt);} cout<<dp[now][0][0];return0;}
B
B
B
考虑两个
11
11
11 直接不可能到达,状态较少,可以通过
#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N =80;int Mod, iv2, iv4;intadd(int a,int b){return a + b >= Mod ? a + b - Mod : a + b;}intdec(int a,int b){return a - b <0 ? a - b + Mod : a - b;}intmul(int a,int b){return1ll * a * b % Mod;}
void Add(int&a,int b){ a =add(a,b);}
void Mul(int&a,int b){ a =mul(a,b);}
void Dec(int&a,int b){ a =dec(a,b);}intksm(int a,int b){int as=1;for(;b;b>>=1,Mul(a,a))if(b&1)Mul(as,a);return as;}int n; typedef bitset<N> bs;
namespace table{
cs int M =19260817;int fi[M], nxt[M], v[M], ec; bs w[M];
void ins(bs x,int vl){int t=0;for(int i=0; i<n; i++) t=(ll)(t*31+x[i])%M;
nxt[++ec]=fi[t]; fi[t]=ec,swap(w[ec],x), v[ec]=vl;
t=0;for(int i=n-1;~i;i--) t=(ll)(t*31+x[i])%M;
nxt[++ec]=fi[t]; fi[t]=ec,swap(w[ec],x), v[ec]=vl;}intask(bs x){int t=0;for(int i=0; i<n; i++) t=(ll)(t*31+x[i])%M;for(int e=fi[t];e;e=nxt[e])if(w[e]==x)return v[e];return-1;}}
void cover(bs&x){int l=n+1,r=-1;for(int i=1;i+1<n;i++)if(x[i]&&x[i+1])l=min(l,i),r=max(r,i);for(int i=l; i<=r+1; i++) x.set(i);}intdfs(bs x){if(x.test(0))return0;
x.set(0);cover(x);int t = table::ask(x);if(~t)return t;int ans =0;
bs nx = x>>1;if(x.test(0)) nx.set(n-1);Add(ans,dfs(nx));
nx = x>>2;if(x.test(0)) nx.set(n-2);if(x.test(1)) nx.set(n-1);Add(ans,dfs(nx));
nx = x<<1;if(x.test(n-1)) nx.set(0);Add(ans,dfs(nx));
nx = x<<2;if(x.test(n-1)) nx.set(1);if(x.test(n-2)) nx.set(0);Add(ans,dfs(nx));Mul(ans,iv4);Add(ans,1);return ans;}intmain(){
#ifdef FSYolanda
freopen("1.in","r",stdin);
#endif
scanf("%d%d",&n,&Mod);
iv2 =(Mod+1)>>1; iv4 =mul(iv2,iv2);if(n==1)returnputs("1"),0;
cout <<dfs(bs());return0;}
C
C
C
要维护当前
p
a
m
pam
pam 上一条链的
∑
l
e
n
∗
s
i
z
e
\sum len*size
∑len∗size 把串离线下来,预处理每个点向前向后的最长回文前后缀,实际的最长回文前后缀需要在后缀树上跳到对应的长度,加入一个点对应的链修改,询问是一个链求和,用树剖解决
l
c
a
lca
lca 的贡献特殊处理,若二者的
l
c
p
lcp
lcp 恰是最长公共回文前缀,则
l
c
a
lca
lca 的贡献可以被算到,否则不能被算到