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

21 篇文章 0 订阅
20 篇文章 0 订阅


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

*/



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值