2012 Multi-University Training Contest 6[hdu4350~4359]


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

*/



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章