树形dp
树上背包
// 树上背包
// 关系构成一个DAG图,又每个点只有一个父亲,构成一个森林
// 每棵子树,要想选孩子,根节点是必须要选的。
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn = 305;
vector<vector<int> >G;
int dp[maxn][maxn]; // 到第i号为根的子树中,已经遍历了i号点的前j棵子树,选了k门课的最大学分
int sz[maxn];
// 此处空间优化了第二维
void dfs(int u){
sz[u]=1;
for(auto v:G[u]){
dfs(v);
sz[u]+=sz[v];
for(int j=m+1;j>=1;--j){
// 当前已经选了j
for(int k=0;k<=sz[v]&&j-k>0;++k){
// 当前子树选k个
dp[u][j] = max(dp[u][j-k]+dp[v][k],dp[u][j]);
}
}
}
}
void solve(){
dfs(0); // 答案为m+1是因为,空的根节点必选,而这个节点是捏造的
cout<<dp[0][m+1]<<endl;
}
int main(){
scanf("%d%d",&n,&m);
G.resize(n+4);
for(int i=1;i<=n;++i){
int u,w;scanf("%d%d",&u,&w);
G[u].push_back(i);
dp[i][1] = w;
}
solve();
}
普通DP
2020杭电多校10 Permutation Counting
题意:
思路:
官方题解
其实就是,面对前
i
i
i 个数的排列。我们可以从只有
i
−
1
i-1
i−1 个数的排列中得到。这
i
−
1
i-1
i−1 个数的排列不一定是
1
,
2
,
.
.
.
,
i
−
1
1,2,...,i-1
1,2,...,i−1的排列,可能是
1
,
2
,
3
,
5
,
6
,
.
.
.
,
i
−
1
1,2,3,5,6,...,i-1
1,2,3,5,6,...,i−1的排列。反正少一个数。但是我们只关注在该排列中第几大。即,以
1
,
2
,
3
,
5
,
6
,
.
.
.
,
i
−
1
1,2,3,5,6,...,i-1
1,2,3,5,6,...,i−1为例,此时6是第5大。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5004;
int b[maxn];
int dp[maxn][maxn];
const int mod = 1e9+7;
void solve(){
int n;scanf("%d",&n);
for(int i=1;i<n;++i) scanf("%d",&b[i]);
for(int i=1;i<=n+3;++i) for(int j=1;j<=n+3;++j) dp[i][j]=0;
dp[1][1] = 1;
for(int i=2;i<=n;++i){
if(b[i-1]) {
for(int j=i;j>=1;--j) dp[i][j] = (dp[i][j+1] + dp[i-1][j])%mod;
}
else {
for(int j=1;j<=i;++j) dp[i][j] = (dp[i][j-1] + dp[i-1][j-1])%mod;
}
}
int ans=0;
for(int i=1;i<=n;++i) ans += dp[n][i], ans%=mod;
cout<<ans<<endl;
}
int main(){
int t;scanf("%d",&t);
while(t--) solve();
}
(伪)动态dp
原型:一个动态规划。(线性齐次常系数)但是中间状态会被修改!所以用矩阵表示状态转移,就可以修改中间矩阵。最后求结果就直接查询(1-n)。
节点为矩阵。
区间修改,全局(只查1-n)查询。
预处理矩阵求幂。
题目:飞马祝福语
给一个字串(1e5),查询该串有多少个为"FeiMa"的子序列。q(1e5)次修改,改一整个子区间为一个字母。同时重复询问。
思路:
把dp转化为矩阵节点线段树。
//
// Created by Artis on 2021/6/1.
//
#include<bits/stdc++.h>
using namespace std;
//#define int long long int
#define mp(a, b) make_pair(a,b)
#define vi vector<int>
#define mii map<int,int>
#define mpi map<pair<int,int>,int>
#define vp vector<pair<int,int> >
#define pb(a) push_back(a)
#define fr(i, n) for((i)=0;(i)<(n);(i)++)
#define rep(i, a, n) for((i)=a;(i)<(n);(i)++)
#define F first
#define S second
#define endl "\n"
#define Endl "\n"
#define fast std::ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define mod 1000000007
#define dom 998244353
#define sl(a) (int)(a).length()
#define sz(a) (int)(a).size()
#define all(a) a.begin(),(a).end()
#define pii pair<int,int>
#define mini 2000000000000000000
#define time_taken (1.0 * clock() / CLOCKS_PER_SEC)
//mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
//const long double pi = acos(-1);
//mt19937_64 mt(chrono::steady_clock::now().time_since_epoch().count());
//primes for hashing 937, 1013
template<typename T, typename U>
static inline void amin(T &x, U y) {
if (y < x)
x = y;
}
template<typename T, typename U>
static inline void amax(T &x, U y) {
if (x < y)
x = y;
}
struct Matrix{
int a[6][6],n=5;
Matrix(const int &N = 5):n(N){memset(a,0,sizeof(a));}
void operator = (const Matrix &x){
n = x.n;
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
a[i][j] = x.a[i][j];
}
Matrix operator * (const Matrix & x) const{
Matrix ans = Matrix(n);
for(int i=0;i<=n;++i)
for(int j=0;j<=n;++j)
for(int k=0;k<=n;++k)
ans.a[i][j] = (ans.a[i][j] +1ll*a[i][k]*x.a[k][j])%dom;
return ans;
}
};
const int maxn = 1e5+5;
int T,n,q,a[maxn];
map<char,int> m;
Matrix pre[6][maxn];
struct Tree{Matrix mul;int tag;}t[maxn<<2];
inline int ls(int k) {return k<<1;}
inline int rs(int k) {return k<<1|1;}
inline void push_up(int l,int r,int k){
t[k].mul = t[ls(k)].mul * t[rs(k)].mul;
}
inline void push_down(int l,int r,int k){
if(t[k].tag != -1){
t[ls(k)].tag = t[k].tag;
t[rs(k)].tag = t[k].tag;
int mid = (l+r)>>1;
t[ls(k)].mul = pre[t[k].tag][mid-l+1]; // 长度
t[rs(k)].mul = pre[t[k].tag][r-mid];
t[k].tag = -1;
}
}
inline void build(int l,int r,int k){
t[k].tag = -1;
if(l==r){
t[k].mul = pre[a[l]][1];
return;
}
int mid = (l+r)>>1;
build(l,mid,ls(k));
build(mid+1,r,rs(k));
push_up(l,r,k);
}
inline void update(int nl,int nr,int l,int r,int k,int x){
if(nl<=l && r<=nr){
t[k].mul = pre[x][r-l+1];
t[k].tag = x;
return;
}
push_down(l,r,k);
int mid = (l+r)>>1;
if(nl<=mid) update(nl,nr,l,mid,ls(k),x);
if(nr>mid) update(nl,nr,mid+1,r,rs(k),x);
push_up(l,r,k);
}
signed main() {
fast;
for(int k=0;k<=5;++k){
for(int i=0;i<=5;++i)
pre[k][0].a[i][i] = pre[k][1].a[i][i]=1;
if(k) pre[k][1].a[k-1][k]=1;
}
for(int k=0;k<=5;++k)
for(int i=2;i<maxn;++i)
pre[k][i]=pre[k][i-1]*pre[k][1];
// 预处理,就不用在更新的时候再做快速幂运算了。
m['F']=1,m['e']=2,m['i']=3,m['M']=4,m['a']=5;
cin>>T;
while(T--){
cin>>n>>q;
for(int i=1;i<=n;++i){
char x;
cin>>x;
a[i]=m[x];
}
build(1,n,1);
while(q--){
int l,r;
char x;
cin>>l>>r>>x;
update(l,r,1,n,1,m[x]);
cout<<t[1].mul.a[0][5]<<endl;
}
}
return 0;
}