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
*/