http://www.lydsy.com/JudgeOnline/problem.php?id=1911
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 1000100 using namespace std; typedef long long int LL; LL n,f[MAXN],sum[MAXN]; //sum[] LL q[MAXN],h,t,a,b,c; double slope(int x,int y) // { return (double)((f[x]+a*sum[x]*sum[x]-b*sum[x])-(f[y]+a*sum[y]*sum[y]-b*sum[y]))/(2*a*(sum[x]-sum[y])); } int main() { scanf("%lld%lld%lld%lld",&n,&a,&b,&c); for(int i=1;i<=n;i++) { LL x; scanf("%lld",&x); sum[i]=sum[i-1]+x; } f[0]=0; h=t=1; q[1]=0; for(int i=1;i<=n;i++) { while(h<t&&slope(q[h+1],q[h])<=sum[i]) h++; f[i]=f[q[h]]+a*(sum[i]-sum[q[h]])*(sum[i]-sum[q[h]])+b*(sum[i]-sum[q[h]])+c; while(h<t&&slope(q[t],q[t-1])>slope(i,q[t])) t--; q[++t]=i; } printf("%lld\n",f[n]); return 0; }
http://www.lydsy.com/JudgeOnline/problem.php?id=1026
首先DP预处理f[][]数组,f[i][j]表示长度为i的数字,最高位为j(可以为0)的windy数个数。
比较显然的思路就是求[a,b]之间的windy数个数,相当于求[1,b+1)中的windy数个数-[1,b+1)中的windy数个数。问题转化为求[1,x)中的windy数个数。我们可以通过取模得到x的每一位的数字以及x的长度cnt,例如x=13579的情况时,我们相当于求
1~9999中的windy数个数(长度小于cnt的windy数个数)+10xxx~13xxx形式的windy数个数+130xx~134xx形式的windy数个数+1350x~1356x的windy数个数+1357x形式的windy数个数。
注意若碰到不合法情况,例如求10xxx~12xxx形式的windy数个数,显然|1-0|<2,|1-2|<2,10xxx~12xxx的数字都不是windy数,就不用继续统计后面的数字了
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 15 #define MAXM 10 using namespace std; typedef long long int LL; int f[MAXN][MAXM]; //f[i][j]=最高位为i位,最高位数字为j的windy数个数 int digit[MAXN],cnt=0; //x有cnt位长,每一位的数字保存在digit数组中,数组中下标1的是个位 void init(int n) //预处理出数字x的每一位,保存在digit数组中 { if(n==0) return; digit[++cnt]=n%10; init(n/10); } void DP() //DP预处理 { for(int i=0;i<=9;i++) f[1][i]=1; //初始化 for(int i=2;i<MAXN;i++) for(int j=0;j<=9;j++) for(int k=0;k<=9;k++) if(abs(j-k)>=2) f[i][j]+=f[i-1][k]; } LL cal(int x) //求[1,x]中的windy数个数 { LL ans=0; cnt=0; memset(digit,0,sizeof(digit)); init(x); //先求出位数不到cnt的windy数个数 for(int i=1;i<cnt;i++) for(int j=1;j<=9;j++) //!!!!!!!!!!! ans+=f[i][j]; for(int i=1;i<digit[cnt];i++) //再加上位数为cnt,最高位比x最高位小的windy数个数 ans+=f[cnt][i]; for(int i=cnt-1;i>0;i--) //枚举剩下的第i位 { for(int j=0;j<digit[i];j++) //枚举第i位为数字j,j要比x的第i位小 if(abs(digit[i+1]-j)>=2) ans+=f[i][j]; if(abs(digit[i+1]-digit[i])<2) break; //x的第i位和第i+1位之差小于2,那么后面的不需要再枚举了 } return ans; } int main() { int a,b; cin>>a>>b; DP(); cout<<cal(b+1)-cal(a)<<endl; return 0; }
http://www.lydsy.com/JudgeOnline/problem.php?id=1040
此题就是没有上司的舞会的基环树林版本。我们可以先用一次DFS求出树林中每棵基环树上可以形成环的那条边E=U->V,或者说是去掉这条边就可以把基环树变成一般的树,然后我们删掉这条边,由于这条边删掉后,U和V中只能取一个或者都不取,因此要分两次进行树上DP,一次以U为起点树上DP,一次以V为起点树上DP。把两次f[U][0]和f[V][0]中取较大者计入答案即可。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <algorithm> #define MAXN 1000100 using namespace std; typedef long long int LL; struct edge { int u,v,next; }edges[MAXN*2]; int head[MAXN],nCount=1; void AddEdge(int U,int V) { edges[++nCount].u=U; edges[nCount].v=V; edges[nCount].next=head[U]; head[U]=nCount; } bool visit[MAXN]; int val[MAXN]; LL f[MAXN][2],ans=0; //f[i][0]=不取点i得到的最大分数,f[i][0]=取点i得到的最大分数 int U,V,E; //去掉边E=(U->V)后,基环树变成了一般的树 void DFS(int u,int last) //找形成基环树的那条边E,last=链接u和父亲的边 { visit[u]=true; for(int p=head[u];p!=-1;p=edges[p].next) { int v=edges[p].v; if((p^1)==last) continue; if(visit[v]) //找到了形成基环树的那条边 { U=u; V=v; E=p; continue; } DFS(v,p); } } void DP(int u,int last,int rolle) //last是连接u父亲和u的边,rolles是构成基环树的边 { f[u][1]=val[u]; //初始化 f[u][0]=0; for(int p=head[u];p!=-1;p=edges[p].next) { int v=edges[p].v; if(p==rolle||(p^1)==rolle) //边p是之前标记的构成基环树的边,不能走 continue; if((p^1)==last) continue; DP(v,p,rolle); f[u][1]+=f[v][0]; f[u][0]+=max(f[v][0],f[v][1]); } } int main() { memset(head,-1,sizeof(head)); nCount=1; int n; scanf("%d",&n); for(int i=1;i<=n;i++) { int v; scanf("%d%d",&val[i],&v); AddEdge(i,v); AddEdge(v,i); } for(int i=1;i<=n;i++) //枚举将每个点作为DFS的起点(每个基环树的根) { if(!visit[i]) { DFS(i,0); DP(U,0,E); LL tmp=f[U][0]; DP(V,0,E); tmp=max(tmp,f[V][0]); ans+=tmp; } } printf("%lld\n",ans); return 0; }