P4910 帕秋莉的手环 
首先你需要明白每一段都可以拼接,因此只需要找到存在“金”的序列个数即可
设 dp 方程 f [ i ] [ j ] f[i][j] f [ i ] [ j ] i i i j j j f [ i ] [ 1 / 0 ] f[i][1/0] f [ i ] [ 1/0 ] i i i 
f [ i ] [ 0 ] = f [ i − 1 ] [ 1 ] f [ i ] [ 1 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] f [ i − 1 ] [ 0 ] = f [ i − 2 ] [ 1 ] f [ i ] [ 1 ] = f [ i − 1 ] [ 1 ] + f [ i − 2 ] [ 1 ] \begin{aligned}
f[i][0]&=f[i-1][1]\\
f[i][1]&= f[i-1][0]+f[i-1][1]\\
f[i-1][0]&=f[i-2][1]\\
f[i][1]&=f[i-1][1]+f[i-2][1]\\
\end{aligned}
 f [ i ] [ 0 ] f [ i ] [ 1 ] f [ i − 1 ] [ 0 ] f [ i ] [ 1 ]  = f [ i − 1 ] [ 1 ] = f [ i − 1 ] [ 0 ] + f [ i − 1 ] [ 1 ] = f [ i − 2 ] [ 1 ] = f [ i − 1 ] [ 1 ] + f [ i − 2 ] [ 1 ]  
因此可以看出来是一个求斐波那契
那就好办了,然后考虑这个环的性质
首先能够贡献答案的有两种情况,一种是两段都是“金”(记做 A A A B B B A + 2 ∗ B A+2*B A + 2 ∗ B 
代码就用一个矩阵快速幂就行了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long using  namespace  std;const  int  modp=1e9 +7 ;int  n;int  f[5 ]={0 ,1 ,1 ,2 ,3 };struct  mat {	int  a[3 ][3 ]; 	mat  (int  aa=0 ,int  bb=0 ,int  cc=0 ,int  dd=0 ) 	{ 		a[1 ][1 ]=aa; 		a[1 ][2 ]=bb; 		a[2 ][1 ]=cc; 		a[2 ][2 ]=dd; 		return  ; 	} }; mat operator  *(mat a,mat b) { 	mat ans; 	int  i,j,k; 	for (i=1 ;i<=2 ;i++) 	for (j=1 ;j<=2 ;j++) 	for (k=1 ;k<=2 ;k++) 	{ 		ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j])%modp; 	} 	return  ans; } int  fib (int  y) 	mat bas=mat (1 ,1 ,1 ,0 ); 	mat ans=mat (1 ,0 ,0 ,1 ); 	mat looker=mat (1 ,0 ,1 ,0 ); 	if (y<=1 ) return  f[y]; 	y--; 	while (y) 	{ 		if (y&1 ) ans=ans*bas; 		bas=bas*bas; 		y>>=1 ; 	} 	looker=looker*ans; 	return  looker.a[1 ][1 ]; } signed  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	int  t; 	cin>>t; 	while (t--) 	{ 		cin>>n; 		cout<<(fib (n)+fib (n-1 )*2 )%modp<<"\n" ;  	} 	return  0 ; } 
P4933 大师 
题意就是求等差数列的个数
刚开始拿到这道题发现用 f [ i ] [ j ] f[i][j] f [ i ] [ j ] i i i j j j 
f [ i ] [ j ] = ∑ k = 1 i − 1 f [ k ] [ a [ i ] − a [ k ] ] f[i][j]=\sum\limits_{k=1}^{i-1}f[k][a[i]-a[k]] f [ i ] [ j ] = k = 1 ∑ i − 1  f [ k ] [ a [ i ] − a [ k ]] 
然后发现有负数,不好处理,用了个map来存
然后调试半个多小时,发现连样例都过不了,输出了一下步骤发现多算了一些值,ans只加增量就过了…就过了
感觉是个恶评题目?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  using  namespace  std;const  int  maxn=20020 ;const  int  modp=998244353 ;map <int ,int > f[1020 ]; int  a[1020 ],ans;int  n,m;int  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	cin>>n; 	for (i=1 ;i<=n;i++) cin>>a[i]; 	for (i=1 ;i<=n;i++) 	{ 		 		ans++;	 		for (j=1 ;j<i;j++) 		{ 			int  looker=f[i][a[i]-a[j]]; 			f[i][a[i]-a[j]]=(f[i][a[i]-a[j]]+f[j][(a[i]-a[j])]+1 )%modp; 			ans=(ans+f[i][a[i]-a[j]]-looker)%modp; 			 			 		} 		 		 	} 	cout<<ans<<endl; 	return  0 ; } 
P5858 「SWTR-03」Golden Sword 
比上面两个都更有难度
题意就很毒瘤,花了好多时间才理解
题目的意思一个容量最大为 ω \omega ω s s s k k k ∑ i − 1 s z s z × a k \sum\limits_{i-1}^{sz} sz \times a_k i − 1 ∑ sz  sz × a k  
刚开始没看到有负数,心想不就是一个sb单调队列,然后第一个样例秒过,然后看到第二个样例,怀疑人生(
设 f [ i ] [ j ] f[i][j] f [ i ] [ j ] i i i j j j 
显然 f [ i ] [ j ] = max  k = j − 1 j + s − 1 f [ i − 1 ] [ k ] f[i][j]=\max\limits_{k=j-1}^{j+s-1} f[i-1][k] f [ i ] [ j ] = k = j − 1 max j + s − 1  f [ i − 1 ] [ k ] O ( s ) O(s) O ( s ) 
对每个 j j j i i i O ( n ω k ) O(n\omega k) O ( nωk ) 
考虑优化,对每个 j j j s s s 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long using  namespace  std;const  int  maxn=5510 ;int  q[maxn*2 ];int  n,a[maxn]; int  f[2 ][maxn];int  m,s;int  mmx[maxn];int  ans;signed  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	cin>>n>>m>>s; 	for (i=1 ;i<=n;i++) cin>>a[i]; 	int  l=1 ,r=0 ; 	memset (f,-0x3f ,sizeof (f)); 	f[0 ][0 ]=0 ; 	for (i=1 ;i<=n;i++) 	{ 		memset (q,0 ,sizeof (q)); 		for (j=0 ;j<=m;j++) f[i&1 ][j]=-0x3f3f3f3f3f3f3f3f ; 		l=1 ,r=0 ; 		for (j=m+1 ;j;j--) 		{ 			while (l<=r&&f[(i-1 )&1 ][q[r]]<=f[(i-1 )&1 ][j-1 ]) r--; 			while (q[l]>j+s-1 &&l<=r) l++; 			q[++r]=j-1 ; 			f[i&1 ][j]=f[(i-1 )&1 ][q[l]]+a[i]*j; 		} 	} 	ans=-0x3f3f3f3f3f3f3f3f ; 	for (i=0 ;i<=m;i++) ans=max (ans,f[n&1 ][i]); 	cout<<ans<<endl; 	return  0 ; } 
首先这题要建模
烹饪方法各不相同,实际可以转化成每一行只能选一个
此时我们设出方程f [ i ] [ j ] [ k ] f[i][j][k] f [ i ] [ j ] [ k ] i i i p o s pos p os j j j k k k 
同时,我们设g [ i ] [ j ] g[i][j] g [ i ] [ j ] i i i j j j 
不难发现总方案数=到最后一行选了任何多个(不包括 0 0 0 
以及 可行方案数=总方案数-∑ j > k f [ i ] [ j ] [ k ] \sum\limits_{j>k}{f[i][j][k]} j > k ∑  f [ i ] [ j ] [ k ] 
上面的方程转移可以由三个方面,第一个是这一行没选,第二个是选了一个 p o s pos p os 
f [ i ] [ j ] [ k ] + = { f [ i − 1 ] [ j ] [ k ] f [ i − 1 ] [ j − 1 ] [ k ] ∗ a [ i ] [ p o s ] f [ i − 1 ] [ j ] [ k − 1 ] ∗ ( s u m [ i ] − a [ i ] [ p o s ] ) f[i][j][k]+=\begin{cases}f[i-1][j][k] \\f[i-1][j-1][k]*a[i][pos]\\f[i-1][j][k-1]*(sum[i]-a[i][pos])
\end{cases}
 f [ i ] [ j ] [ k ] + = ⎩ ⎨ ⎧  f [ i − 1 ] [ j ] [ k ] f [ i − 1 ] [ j − 1 ] [ k ] ∗ a [ i ] [ p os ] f [ i − 1 ] [ j ] [ k − 1 ] ∗ ( s u m [ i ] − a [ i ] [ p os ])  
容斥一下就行了,可是这个的复杂度是 O ( n 3 m ) O(n^3m) O ( n 3 m ) 
考虑性质,对答案有贡献的时候 j ≥ k j\geq k j ≥ k 
(不过我代码里面的好像是 d p [ i ] [ k ] [ j ] dp[i][k][j] d p [ i ] [ k ] [ j ] 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long using  namespace  std;const  int  maxn=2020 ;const  int  modp=998244353 ;int  f[maxn][maxn*2 ];int  s[maxn][maxn];int  a[maxn][maxn];int  g[maxn][maxn];int  n,m,ans;signed  main () 	ios::sync_with_stdio (false ); 	int  i,j,delta; 	cin>>n>>m; 	for (i=1 ;i<=n;i++) 	for (j=1 ;j<=m;j++) 	{ 		cin>>a[i][j]; 		s[i][j]=a[i][j]+s[i][j-1 ]; 		s[i][j]%=modp; 	} 	int  looker=n; 	for (j=1 ;j<=m;j++) 	{ 		f[0 ][0 +n]=1 ; 		for (i=1 ;i<=n;i++) 		{ 			for (delta=-i+n;delta<=i+n;delta++) 			{ 				f[i][delta]=(f[i-1 ][delta]+1ll *f[i-1 ][delta-1 ]*a[i][j]%modp+1ll *f[i-1 ][delta+1 ]*(s[i][m]-a[i][j]+modp)%modp)%modp; 				if (delta-looker>0 &&i==n) ans=(ans-f[i][delta]+modp)%modp; 				 			} 		} 	} 	g[0 ][0 ]=1 ; 	for (i=1 ;i<=n;i++) g[i][0 ]=1 ; 	for (i=1 ;i<=n;i++) 	for (j=1 ;j<=n;j++) 	{ 		g[i][j]=(g[i-1 ][j]+1ll *g[i-1 ][j-1 ]*(s[i][m]%modp))%modp; 		 		if (i==n) ans=(ans+g[i][j]+modp)%modp; 	} 	cout<<ans<<endl; 	 	return  0 ; } 
P2760 科技庄园 
一个裸的二进制拆分背包,注意体力不能为 0 0 0 − 1 -1 − 1 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long using  namespace  std;const  int  maxn=200010 ;int  a[maxn],aaa[maxn];int  deg[maxn];int  f[maxn];int  w[maxn];int  tot=0 ;int  n,m,aa,bb;int  ans=0 ;map <pair<int ,int >,int > mp; signed  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	cin>>n>>m>>aa>>bb; 	aa=min (aa,bb-1 ); 	for (i=1 ;i<=n;i++) 	for (j=1 ;j<=m;j++) 	{ 		cin>>bb; 		if (bb) mp[make_pair (i,j)]=++tot,aaa[tot]=bb; 	} 	tot=0 ; 	for (i=1 ;i<=n;i++) 	for (j=1 ;j<=m;j++) 	{ 		cin>>bb; 		if (mp[make_pair (i,j)]) 		{ 			int  looker=mp[make_pair (i,j)]; 			int  k; 			for (k=1 ;k<=bb;k*=2 ) 			{ 				bb-=k;  				w[++tot]=(i+j)*2 *k,a[tot]=aaa[looker]*k,deg[tot]=k; 			} 			if (bb) w[++tot]=(i+j)*2 *(bb),a[tot]=aaa[looker]*(bb),deg[tot]=(bb); 		} 	} 	for (i=1 ;i<=tot;i++) 	for (j=aa;j>=w[i];j--) 	{ 		f[j]=max (f[j-w[i]]+a[i],f[j]); 		ans=max (ans,f[j]); 	} 	cout<<ans<<endl; 	return  0 ; 	 } 
P1799 数列 
删除一些数,求最后剩下的数列中最多能有多少个数在自己的位置上,即 $ Ai=i$ 最多能有多少。
一个序列中贡献答案的一定单调上升,那就好办了,我们类似于求LIS的那种方法找到所有单调上升的序列来刷新答案就行了
注意负数和 a i ≥ i a_i\geq i a i  ≥ i 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  using  namespace  std;const  int  maxn=1010 ;int  f[maxn];int  n,m,a[maxn],p[maxn];int  tot,ans;int  vis[maxn];int  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	cin>>n; 	int  tmp; 	for (i=1 ;i<=n;i++)  	{ 		cin>>a[i]; 		p[i]=i; 	} 	for (i=1 ;i<=n;i++) 	{ 		if (!(a[i]>0 &&a[i]<=i)) continue ; 		for (j=0 ;j<i;j++) 		{ 			if (a[i]>a[j]&&a[i]-a[j]<=p[i]-p[j]) f[i]=max (f[j]+1 ,f[i]); 		} 		ans=max (ans,f[i]); 	} 	cout<<ans<<endl; 	return  0 ; } 
P6419 [COCI2014-2015#1] Kamp 
一个换根 dp \text{dp} dp 
首先设
g [ u ] g[u] g [ u ] u u u u u u 
l 1 [ t ] , l 2 [ u ] l1[t],l2[u] l 1 [ t ] , l 2 [ u ] u u u 
i d [ t ] id[t] i d [ t ] i d id i d 
 
然后我们考虑第一次 dfs \text{dfs} dfs 
如果 u u u j j j g [ u ] = ∑ v ∈ s o n u g [ v ] + v a l ( e u , v ) g[u]=\sum\limits_{v\in son_u}{g[v]+val(e_{u,v})} g [ u ] = v ∈ so n u  ∑  g [ v ] + v a l ( e u , v  ) 
否则不考虑
 
然后考虑第二次 dfs \text{dfs} dfs 
我们设 f [ u ] f[u] f [ u ] u u u 
当子树内没有人的家时,f [ v ] = f [ u ] + 2 ∗ v a l ( e u , v ) f[v]=f[u]+2*val(e_{u,v}) f [ v ] = f [ u ] + 2 ∗ v a l ( e u , v  ) 
当只有子树内有家时,f [ u ] = g [ u ] f[u]=g[u] f [ u ] = g [ u ] 
否则,无论以哪个点为根,我们都要经过这个点 f [ t ] = f [ v ] f[t]=f[v] f [ t ] = f [ v ] 
最后显然是不走回来更优,因此我们维护最长链
l 1 [ u ] + v a l ( e i , j ) ≥ l 1 [ j ] , i d [ u ] ≠ j l1[u]+val(e_{i,j})\geq l1[j] ,id[u]\neq j l 1 [ u ] + v a l ( e i , j  ) ≥ l 1 [ j ] , i d [ u ]  = j 
l 1 [ u ] + v a l ( e i , j ) ≥ l 2 [ j ] , i d [ u ] ≠ j l1[u]+val(e_{i,j})\geq l2[j] ,id[u]\neq j l 1 [ u ] + v a l ( e i , j  ) ≥ l 2 [ j ] , i d [ u ]  = j 
l 2 [ u ] + v a l ( e i , j ) ≥ l 1 [ j ] l2[u]+val(e_{i,j})\geq l1[j] l 2 [ u ] + v a l ( e i , j  ) ≥ l 1 [ j ] 
l 2 [ u ] + v a l ( e i , j ) ≥ l 2 [ j ] l2[u]+val(e_{i,j})\geq l2[j] l 2 [ u ] + v a l ( e i , j  ) ≥ l 2 [ j ] 
至于为什么是正确的,因为 i d [ u ] = j id[u]=j i d [ u ] = j l 1 , l 2 l1,l2 l 1 , l 2 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long using  namespace  std;const  int  maxn=500500 ;int  sz[maxn],l1[maxn],l2[maxn],head[maxn],f[maxn],g[maxn];int  id[maxn],n,m,size;struct  edge {	int  next,to,dis; }e[maxn*2 ]; inline  void  addedge (int  next,int  to,int  dis) 	e[++size].to=to; 	e[size].dis=dis; 	e[size].next=head[next]; 	head[next]=size; } void  dfs1 (int  t,int  fat) 	int  i,j,k; 	for (i=head[t];i;i=e[i].next) 	{ 		j=e[i].to; 		k=e[i].dis; 		if (j==fat) continue ; 		dfs1 (j,t); 		if (!sz[j]) continue ; 		if (l1[t]<=l1[j]+k) 		{ 			l2[t]=l1[t]; 			l1[t]=l1[j]+k; 			id[t]=j; 		} 		else  if (l2[t]<l1[j]+k) l2[t]=l1[j]+k; 		sz[t]+=sz[j]; 		g[t]+=g[j]+k*2 ; 	} } void  dfs2 (int  t,int  fat) 	int  i,j,k; 	for (i=head[t];i;i=e[i].next) 	{ 		j=e[i].to; 		k=e[i].dis; 		if (j==fat) continue ; 		if (!sz[j]) 		{ 			f[j]=f[t]+2 *k; 			l1[j]=l1[t]+k; 			dfs2 (j,t); 			continue ; 		} 		if (sz[j]==m)  		{ 			f[j]=g[j]; 			dfs2 (j,t); 			continue ; 		} 		if (l1[t]+k>l1[j]&&id[t]!=j) l2[j]=l1[j],l1[j]=l1[t]+k,id[j]=t; 		else  if (l2[t]+k>l1[j]) l2[j]=l1[j],l1[j]=l2[t]+k,id[j]=0 ; 		else  if (l1[t]+k>l2[j]&&id[t]!=j) l2[j]=l1[t]+k; 		else  if (l2[t]+k>l2[j]) l2[j]=l2[t]+k; 		f[j]=f[t]; 		dfs2 (j,t); 	} } signed  main () 	ios::sync_with_stdio (false ); 	register  int  i,j; 	cin>>n>>m; 	int  t1,t2,t3; 	for (i=1 ;i<n;i++) cin>>t1>>t2>>t3,addedge (t1,t2,t3),addedge (t2,t1,t3); 	for (i=1 ;i<=m;i++) cin>>t1,sz[t1]=1 ; 	dfs1 (1 ,1 ); 	f[1 ]=g[1 ]; 	dfs2 (1 ,1 ); 	for (i=1 ;i<=n;i++) cout<<f[i]-l1[i]<<endl; 	return  0 ; } 
P1131 [ZJOI2007]时态同步 
在摸机箱的时候突然想到:
设 f [ u ] f[u] f [ u ] u u u 
显然一颗子树如果不满足时态同步那么就一直不满足了。
然后刚开始 d f s dfs df s 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <stdio.h>  #include <iostream>  #include <cmath>  #include <cstring>  #include <queue>  #include <stack>  #include <vector>  #include <set>  #include <map>  #include <algorithm>  #define  int long long  using  namespace  std;const  int  maxn=500500 ;int  f[maxn],head[maxn],size;int  n,m,st;int  dismmx[maxn];struct  edge {	int  next,to,dis; }e[maxn*2 ]; inline  void  addedge (int  next,int  to,int  dis) 	e[++size].to=to; 	e[size].dis=dis; 	e[size].next=head[next]; 	head[next]=size; } void  dfs1 (int  t,int  fat) 	int  emmx=0 ,i,j,k; 	for (i=head[t];i;i=e[i].next)  	{ 		j=e[i].to; 		k=e[i].dis; 		if (j==fat) continue ; 		dfs1 (j,t); 		emmx=max (dismmx[j]+k,emmx); 	} 	dismmx[t]=emmx; 	for (i=head[t];i;i=e[i].next) 	{ 		j=e[i].to; 		k=e[i].dis; 		if (j==fat) continue ; 		f[t]+=f[j]+dismmx[t]-(dismmx[j]+k); 	} } signed  main () 	register  int  i,j; 	 	scanf ("%d" ,&n); 	 	scanf ("%d" ,&st); 	int  t1,t2,t3; 	 	for (i=1 ;i<n;i++) scanf ("%d %d %d" ,&t1,&t2,&t3),addedge (t1,t2,t3),addedge (t2,t1,t3); 	dfs1 (st,st); 	printf ("%lld\n" ,f[st]); 	return  0 ; }