4351 Digital root
4355 Party All the Time
两种解法, 一种三分,一种dp
三分是要满足凹凸性的, 既二阶导数不变号, 这个很好证明的,
题目要求的是sigma|p-xi|^3*wi, 不妨对每项拆开讨论 :
|p-xi|^3*wi ,当p>=xi时 原式为(p-xi)^3*wi 二阶导为(6p-6xi)*wi,恒大于等于0, p<xi同理, 故为凸函数, 由于凸函数加凸函数 还是凸函数, 三分显然是正确的。
dp的话, 就是预处理, 求每一点是直接求出,没有转移,然后求任意点之间的, 直接解2次方程就好, 这个好久没写过类似代码了, 出了好多错, 最后还输出-0wa了2次
using namespace std; const int mod=1000000007; const int maxn=50000+123; double x[maxn], w[maxn]; double l[4][maxn], r[4][maxn]; double dp[maxn]; double sum(int i, double xi) { double res=0.0; res+=xi*xi*xi*l[0][i]-3*xi*xi*l[1][i]+3*xi*l[2][i]-l[3][i]; res+=r[3][i+1]-3*xi*r[2][i+1]+3*xi*xi*r[1][i+1]-xi*xi*xi*r[0][i+1]; return res; } double getmin(int i, int j) { double a=l[0][i]-r[0][j], b=-3.0*(l[1][i]-r[1][j]), c=3.0*(l[2][i]-r[2][j]); a*=3., b*=2.; double delta=b*b-4.0*a*c; double res=min(dp[i], dp[j]);/// ¶Ëµã if(zero(a)) { if(!zero(b)) { double x1=-c/b; if(x1>=x[i] && x1<=x[j])res=min(res, sum(i, x1)); //printf("%lf a==0 b!=0 %lf\n", x1, res); } }else if(delta>0) { double x1=(-b+(sqrt(delta)))/(2.0*a); double x2=(-b-(sqrt(delta)))/(2.0*a); if(x1<=x[j] && x1>=x[i])res=min(res, sum(i, x1)); if(x2<=x[j] && x2>=x[i])res=min(res, sum(i, x2)); ///printf("%lf a!=0 %lf\n", x1, sum(i, x1)); } //printf("res=%lf\n", res); return res; } int main () { ///freopen("1006.in", "r", stdin); int cas; scanf("%d", &cas); for (int I=1; I<=cas; ++I) { int n; scanf("%d", &n); for (int i=0; i<n; ++i) scanf("%lf%lf", x+i, w+i); clean(l, 0); clean(r, 0); for (int i=0; i<n; ++i) { double tmp=w[i]; for (int j=0; j<4; ++j) { if(i)l[j][i]=l[j][i-1]; l[j][i]+=tmp; tmp*=x[i]; } } for (int i=n-1; i>=0; --i) { double tmp=w[i]; for (int j=0; j<4; ++j) { r[j][i]=r[j][i+1]+tmp; tmp*=x[i]; } } double ans=infll*10.0; for (int i=0; i<n; ++i) { dp[i]=sum(i, x[i]); ///printf("%lf\n", dp[i]); ans=min(ans, dp[i]); } for (int i=0; i<n-1; ++i) { ans=min(ans,getmin(i, i+1)); } printf("Case #%d: %.0lf\n", I, ans+eps);///不加eps会输出-0, 囧 } return 0; }
4358 Boring counting
1.启发式合并 其实就是暴力, 但是在合并时选择从小的合并到大的里, 直接dfs一遍统计出所有的节点的答案, 然后o(1)查询即可, 不过可能是我dfs写的垃圾了, 标程在本地跑暴栈, 交到hdu上ac,我在本地跑暴栈, 交上去还是暴栈, 果断改成手工栈, ToT
2. 将树形结构转成线性结构, 对子树的查询就变为对点的查询。
#include <stdio.h> #include <string.h> #include <map> #include <stack> #include <algorithm> #define fst first #define scd second #define clean(a, x) memset (a, x, sizeof(a)); #define copy(a, b) memcpy(a, b, sizeof(a)); #pragma comment(linker, "/STACK:102400000,102400000") using namespace std; const int maxn=100000+10; #define vex edge[p].v int w[maxn]; struct Edge{int v, next;}edge[2*maxn]; int head[maxn], cnt; void addedge(int u, int v){edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++;} ///*** graphic theory***/// int f[maxn], ans[maxn]; map <int , int> node[maxn]; map<int , int>:: iterator it, pp; int n, k; void merge(int a, int b) { int sa=node[f[a]].size(), sb=node[f[b]].size(); // printf("v==%d u=%d\n", a, b); if(sa>sb) { ans[b]+=ans[a]; for (it=node[f[b]].begin(); it!=node[f[b]].end(); ++it) { pp=node[f[a]].find(it->fst); if(pp!=node[f[a]].end()) { if(pp->scd==k)ans[b]--; if(it->scd==k)ans[b]--; pp->scd+=it->scd; if(pp->scd==k)ans[b]++; } else {/// not exist node[f[a]][it->fst]=it->scd; ///if(it->scd==k)ans[b]++; } } node[f[b]].clear(); f[b]=f[a]; } else { for (it=node[f[a]].begin(); it!=node[f[a]].end(); ++it) { pp=node[f[b]].find(it->fst); if(pp!=node[f[b]].end()) { if(pp->scd==k)ans[b]--; pp->scd+=it->scd; if(pp->scd==k)ans[b]++; } else { node[f[b]][it->fst]=it->scd; if(it->scd==k)ans[b]++; } } node[f[a]].clear(); } } //void dfs(int u, int fa) //{ // node[f[u]][w[u]]=1; // if(k==1)ans[u]++; // for (int p=head[u]; ~p; p=edge[p].next) // { // if(vex==fa)continue; // dfs(vex, u); // merge(vex, u); // } //} stack<int> S; bool vis[maxn]; int rec[maxn], pre[maxn]; void DFS() { int u, v, p; S.push(1); node[f[1]][w[1]]=1; if(k==1)ans[1]++; pre[1]=-1; copy(rec, head); while (!S.empty()) { u=S.top(); vis[u]=true; bool flag=false; for (p=rec[u]; ~p; p=edge[p].next) { if(vis[vex])continue; S.push(vex); node[f[vex]][w[vex]]=1; if(k==1)ans[vex]++; pre[vex]=u; rec[u]=edge[p].next; flag=true; break; } if(flag)continue; if(~pre[u]) { merge(u, pre[u]); } S.pop(); // if(p==-1) // { // for (p=rec[u]; ~p; p=edge[p].next) // { // if(vis[vex])continue; // merge(u, vex); // } // S.pop(); // } } } void init() { clean(head, -1); cnt=0; clean(ans, 0); clean(vis, false); for (int i=1; i<=n; ++i) f[i]=i; for (int i=1; i<=n; ++i) node[i].clear(); } int main() { // freopen("1009.in", "r", stdin); // freopen("1009a.out", "w", stdout); int cas; scanf("%d", &cas); for (int I=1; I<=cas; ++I) { scanf("%d%d", &n, &k); for (int i=1; i<=n; ++i) scanf("%d", w+i); init(); for (int i=1; i<n; ++i) { int u, v; scanf("%d%d", &u, &v); addedge(u, v); addedge(v, u); } // dfs(1, -1); DFS(); if(I>1)puts(""); printf("Case #%d:\n", I); int Q; scanf("%d", &Q); while (Q--) { int q; scanf("%d", &q); printf("%d\n", ans[q]); } } return 0; } /* 4 4 2 1 2 2 3 1 2 1 3 3 4 4 4 2 1 3 4 1 1 2 2 3 1 2 1 3 3 4 4 4 2 1 3 3 1 1 2 2 1 2 1 3 3 2 1 3 3 2 1 2 2 1 2 1 3 3 2 1 3 */
hdu 4359 一道很简单的dp就因为维护前缀和的时候只维护到了最后一个有值的点, 下午比赛卡了2小时, 都没心情做其他题了。
还是和很多2叉树计数一样, 2个状态节点数,和高度。 这里有个前缀和的优化, 一定要注意前缀和的处理!
using namespace std; const int mod=1000000007; ll com[400][400]; ll dp[400][400]; ll sum[400][400]; bool can(int ns, int h) { if(h>8)return ns>=h; return (1<<h)-1 >= ns && ns>=h; } void init() { clean(com, 0); com[0][0]=1; com[1][0]=1; com[1][1]=1; for(int i=2; i<400; ++i) { com[i][0]=1; for (int j=1; j<=i; ++j) com[i][j]=(com[i-1][j]+com[i-1][j-1])%mod; } } const int maxn=380; int main () { init(); clean(dp, 0); clean(sum, 0); dp[0][0]=1; dp[1][1]=1; dp[1][0]=0; sum[0][0]=1; sum[1][1]=1; for (int i=1; i<=maxn; ++i)sum[0][i]=sum[1][i]=1;///!!!!! int cas; scanf("%d", &cas); for (int i=2; i<=maxn; ++i) { for (int j=2; j<=maxn; ++j) { sum[i][j]=sum[i][j-1]; if(!can(i, j))continue; ll res=0; if(can(i-1, j-1))res+=dp[0][0]*dp[i-1][j-1]%mod*2%mod; for (int k=j-1; k+1<i; ++k) { res+=((dp[k][j-1]*sum[i-k-1][j-1])%mod*(com[i-2][k]))%mod; res+=((dp[k][j-1]*sum[i-k-1][j-2])%mod*(com[i-2][i-k-1]))%mod; //res+=((dp[k][j-1]*dp[i-k-1][j-1])%mod*(com[i-2][k]))%mod; res%=mod; } dp[i][j]=res*i%mod; ///if(dp[i][j]<0)printf("%d %d\n", dp[][]) sum[i][j]=(sum[i][j-1]+dp[i][j])%mod; } } for (int I=1; I<=cas; ++I) { int a, b; scanf("%d%d", &a, &b); printf("Case #%d: %I64d\n", I, dp[a][b]%mod); } return 0; } /* 10 3 2 1 1 2 2 4 3 5 4 6 3 360 360 5 5 7 4 */