1 [P1115]最大子段和
solution
dp[i]=max{sum[i]-max{sum[j]}}
一个(O(n))的优秀做法
2 [P1855]榨取kkksc03
solution
dp[i][j]=max(dp[i][j],dp[i-m[k]][j-t[k]]
一个多维的背包。
3 [P1832]A+B Problem升级'
solution
一个完全背包。
dp[i]=dp[i]+dp[i-prime[j]]
4 [P1216][USACO1.5][IOI1994]数字三角形 Number Triangles
solution
a[i][j]+=max(a[i-1][j-1],a[i-1][j]),ans=max(ans,a[i][j]);
5 [P1470][USACO2.3]最长前缀 Longest Prefix
solution
从末尾开始截取子串,set可以去重排序。
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <set>
#include <iostream>
using namespace std;
int read(){
int op=1,a=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
const int maxn=2e5+5;
int m,dp[maxn],ans;
set<string> s[25];
int main(){
string tp;
while(cin>>tp){
if(tp==".") break;
s[tp.size()].insert(tp);
m=max(m,int(tp.size()));
}
dp[0]=1;
string n;
n=" ";
while(cin>>tp) n=n+tp;
for(int i=1;i<n.size();i++){
for(int j=min(i,m);j>=1;j--){//m是最长子串的长度
string tt=n.substr(i-j+1,j);//截取
if(s[tt.size()].count(tt)==1&&dp[i-j]==1)
{ans=i,dp[i]=1;break; }
}
}
printf("%d",ans);
return 0;
}
6 [P2642]双子序列最大和
solution
分别从前和从后求,再枚举每个位置。(O(n))的巧妙做法。
code
#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
long long read(){
long long x=1,a=0;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') x=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){a=a*10+ch-'0';ch=getchar();}
return x*a;
}
const int maxn=1e6+10;
long long a[maxn],f[maxn],l[maxn];
long long n;
int main(){
n=read();
for(int i=1;i<=n;i++) a[i]=read();
f[1]=a[1];
for(int i=2;i<=n;i++) f[i]=max(f[i-1],0ll)+a[i];
for(int i=2;i<=n;i++) f[i]=max(f[i-1],f[i]);
l[n]=a[n];
for(int i=n-1;i>=1;i--) l[i]=max(l[i+1],0ll)+a[i];
for(int i=n-1;i>=1;i--) l[i]=max(l[i+1],l[i]);
long long ans=f[1]+l[3];
for(int i=3;i<n;i++) ans=max(ans,f[i-1]+l[i+1]);
printf("%lld",ans);
return 0;
}
7 [P2782][NOIp2001]友好城市
solution
最长不下降。使用lower_bound
降低复杂度。
8 [P1439]【模版】最长公共子序列
solution
使用map
数组来映射位置。
code
#include <bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
int main() {
ll ans, m;
for (int i = 1; i <= 3000000; i++) {
m = read();
ans ^= m;
}
printf("%lld", ans);
return 0;
}
9 换根dp
solution
我们可以利用一些技巧来把这一类问题优化到 (Theta(n))的时间内解决
接下来,我们就要引入换根dp的一些思想 (或者叫套路) 了
换根dp一般分为三个步骤
1、先指定一个根节点
2、一次dfs统计子树内的节点对当前节点的贡献
3、一次dfs统计父亲节点对当前节点的贡献并合并统计最终答案
光这么说可能不太好理解,我们来结合上面的那个例子看看吧
我们先来看一个节点(u)的子树里面的节点对它的贡献:
很明显,这个贡献就是子树里所有节点到(u)的深度和,我们把它记为 (g_u) (不过待会会发现根本不需要这个 gug_ugu)
接着,我们记 (sz_u) 为以 (u) 为根的子树大小,(dep_u)为点(u) 到 1 号节点(我们指定的根节点)的深度(之后有用)
接下来,我们来考虑第2次dfs,也就是计算父亲对它的贡献
我们令 (f_u) 为以 (u) 为根节点的深度和
所以,我们令 (v) 为 (u)的一个儿子节点,可得(f_v=g_v+(f_u-(g_v+sz_v))+(sz_1-sz_v))
为啥打上了括号呢?是因为这样更好理解了
如果看不懂,我来解释一下
(g_v) 是以(v)为根的子树深度和,显然要加上
fuf_ufu 是父亲 uuu 节点的答案,显然要减去我们 vvv 子树里的信息(不然就多算了)
那么, (g_v+sz_v)就是我们(v)子树的信息了
有人就要问了,子树里的的信息不就是(g_v)吗?
但是我们是从父亲的答案减去,显然在以 (u) 为根时, (v) 的子树中的所有节点的深度都有加一,于是就增加 (sz_v)
同理,后面的(sz_1-sz_v) 就是非 (v) 节点子树中的点啦,和上面一样理解一下就好啦qwq
解释完了,我们化简一下这个柿子 (f_v=f_u+sz_1-2 imes sz_v)
发现可以不用记录 (g)
code
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#include <bitset>
using namespace std;
long long read(){
long long op=1,a=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-') op=-1;c=getchar();}
while(c>='0'&&c<='9'){a*=10,a+=c^48,c=getchar();}
return a*op;
}
const int maxn=1e6+100;
struct node{
int to,nxt;
}e[maxn<<1];
long long n,cnt,id,ans;
long long head[maxn],f[maxn],dep[maxn],siz[maxn];
inline void add(int u,int v){
e[++cnt].nxt=head[u];
head[u]=cnt;
e[cnt].to=v;
}
void dfs1(int x,int fa){
siz[x]=1;dep[x]=dep[fa]+1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
dfs1(y,x);
siz[x]+=siz[y];
}
}
void dfs2(int x,int fa){
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(y==fa) continue;
f[y]=f[x]+n-2*siz[y];
dfs2(y,x);
}
}
int main(){
n=read();
for(int i=1;i<n;i++){
int u,v;u=read(),v=read();
add(u,v),add(v,u);
}
dfs1(1,0);
for(int i=1;i<=n;i++) f[1]+=dep[i];
dfs2(1,0);
for(int i=1;i<=n;i++) if(ans<f[i]) ans=f[i],id=i;
printf("%lld",id);
return 0;
}