2012 Multi-University Training Contest 4

1004 hdu 4334 http://acm.hdu.edu.cn/showproblem.php?pid=4334

题意:

给定五个集合,从五个集合中分别取出5个数,如果存在满足a1 + a2 + a3 + a4 +a5 == 0  输出yes,否则no; 集合的大小小于200 ai的取值为 [-10^15, 1 0^15]

思路:

开始的时候O(n^2)求出前两个集合的所有可能的和,然后O(n^3)+二分log(10^4)做,结果竟然tle。。坑爹啊。。。。最后用了hash过的。好像poj有个类似的题目,也是hash做的。

hash开散列两种实现方式,

挂链:

View Code
#include <iostream>

#include <cstdio>

#include <cmath>

#include <vector>

#include <cstring>

#include <algorithm>

#include <string>

#include <set>

#include <ctime>

#include <queue>

#include <map>

#include <sstream>

#define maxn 177777

#define ll __int64

using namespace std;

ll num[6][205];

struct node

{

    ll num;

    node *next;

}*p[maxn + 10],head[40015];



int main()

{

    //freopen("d.txt", "r", stdin);

     int t,i,j,k;

     int n;

     scanf("%d",&t);

     while (t--)

     {

         memset(p,0,sizeof(p));

         scanf("%d",&n);

         for (i = 0; i < 5; ++i)

         {

             for (j = 0; j < n; ++j) scanf("%I64d",&num[i][j]);

         }

         int pos = 0;

         for (i = 0; i < n; ++i)

         {

             for (j = 0; j < n; ++j)

             {

                ll tmp = num[0][i] + num[1][j];

                int tp = tmp%maxn;

                if (tp < 0) tp = -tp;

                node *q = &head[pos++];

                q->num = tmp;

                q->next = p[tp];

                p[tp] = q;



             }

         }

         bool flag = true;

         for (i = 0; i < n && flag; ++i)

         {

             for (j = 0; j < n && flag; ++j)

             {

                 for (k = 0; k < n && flag; ++k)

                 {

                    ll tmp = (num[2][i] + num[3][j] + num[4][k])*(-1.0);

                     int tp = tmp%maxn;

                     if (tp < 0) tp = -tp;

                      node *q;

                    for (q = p[tp]; q != NULL; q = q->next)

                    {

                        if (q->num == tmp)

                        {

                            flag = false;

                            break;

                        }

                    }

                 }

             }

         }

         if (!flag) puts("Yes");

         else puts("No");

     }

     return 0;

}

vector:

View Code
#include <iostream>

#include <cstdio>

#include <cmath>

#include <cstring>

#include <vector>

#define maxn 17777

#define ll __int64

using namespace std;



vector<ll>vc[maxn];

ll num[6][205];



int main()

{

   //freopen("d.txt","r",stdin);

    int t,i,j,n,k;

    scanf("%d",&t);

    while (t--)

    {

        scanf("%d",&n);

        for (i = 0; i < 5; ++i)

        {

            for (j = 0; j < n; ++j) scanf("%I64d",&num[i][j]);

        }

        for (i = 0; i < maxn; ++i) vc[i].clear();



        for (i = 0; i < n; ++i)

        {

            for (j = 0; j < n; ++j)

            {



                ll tmp = num[0][i] + num[1][j];

                int tp = tmp%maxn;

                if (tp < 0) tp = -tp;

                vc[tp].push_back(tmp);

            }

        }

        bool flag = true;

        for (i = 0; i < n && flag; ++i)

        {

            for (j = 0; j < n && flag; ++j)

            {

                for (k = 0; k < n && flag; ++k)

                {

                    ll tmp = (-1.0)*(num[2][i] + num[3][j] + num[4][k]);

                    int tp = tmp%maxn;

                    if (tp < 0) tp = - tp;

                    int sz = vc[tp].size();

                    for (int ki = 0; ki < sz; ++ki)

                    {

                        if (vc[tp][ki] == tmp)

                        {

                            flag = false;

                            break;

                        }

                    }

                }

            }

        }

        if (!flag) puts("Yes");

        else puts("No");

    }

    return 0;

}

 

1006 hdu 4336 http://acm.hdu.edu.cn/showproblem.php?pid=4336

题意:

给出n种不同卡片在买的小吃力里面出现的可能,求凑齐n种卡片要买的小吃的平均数量。

思路:

根据官方解题报告做的:

设卡片的分布p=(p1,p2,...,pn),T(p)表示拿到所有卡片时买的零食数目,有

由容斥原理得,

由于我没坐过状态压缩的题目,所以这里的代码不大好理解。

View Code
#include <cstdio>

#include <iostream>

using namespace std;



const int maxn = 22;

double p[maxn];



int main() {



   int n,i,j;

   while (~scanf("%d",&n))

   {

       for (i = 0; i < n; ++i) scanf("%lf",&p[i]);



       double ans = 0.0;

       //根据二项式定理C(n,0)+C(n,1) + ... + C(n,n) = 2^n

       //所以这里2^n - 1种可能,枚举

       for (i = 1; i < (1<<n); ++i)

       {

           int ct = 0;

           double tmp = 0.0;

           for (j = 0; j < n; ++j)

           {

               if (i&(1<<j))//检查0到n中存在于i状态的点

               {

                   ct++;

                   tmp += p[j];

               }

           }

           //鸽巢定理

           if (ct&1) ans += 1.0/tmp;

           else ans -= 1.0/tmp;

       }

       printf("%.4lf\n",ans);

   }

    return 0;

}

 

1007 hdu 4337 http://acm.hdu.edu.cn/showproblem.php?pid=4337

虎哥在最后的几分钟内,写了个爆搜竟然过了,orz虎哥啊。。。我也用爆搜写过的。。。

View Code
#include <iostream>

#include <cstdio>

#include <cmath>

#include <cstring>

#define maxn 155

using namespace std;



int map[maxn][maxn],res[maxn];

bool vt[maxn];

int n,m;

bool flag;



void dfs(int pos,int num)

{

    int i;

    if (flag) return ;

    res[num] = pos;

    for (i = 1; i <= n; ++i)

    {

        if (map[pos][i])

        {

            //printf(">>%d\n",i);

            if (!vt[i])

            {

                 //printf("***%d\n",i);

                vt[i] = true;

                dfs(i,num + 1);

                vt[i] = false;

            }

            else if (num + 1 == n + 1 && i == 1)

            {

                flag = true;

                break;

            }

        }

        if (flag) return ;

    }

}

int main()

{

    //freopen("d.txt","r",stdin);

    int i,x,y;

    while (~scanf("%d%d",&n,&m))

    {

        flag = false;

        memset(map,0,sizeof(map));

        memset(vt,false,sizeof(vt));



        for (i = 0; i < m; ++i)

        {

            scanf("%d%d",&x,&y);

            map[x][y] = map[y][x] = 1;

        }

        vt[1] = true;

        dfs(1,1);

        if (flag)

        {

            for (i = 1; i < n; ++i)

            printf("%d ",res[i]);

            printf("%d\n",res[i]);

        }

        else

        {

            printf("no solution\n");

        }

    }

    return 0;

}

1009 hdu 4339 http://acm.hdu.edu.cn/showproblem.php?pid=4339

一看就知道是线段树的题目,本来以为自己线段树还可以,于是就很有信心的做了敲出来了一看题意看错了,我求了成两个字符串形同的连续一样的字符串了的个数了。

赛后敲了一下,终于A了,其实处理一下就是一个很简单的单点更新的题目;郁闷啊。。。

不同的点标为1相同的标为0询问时只要求出该店左边不同的点个数,然后再求下一个不同点的位置即可。

View Code
#include <iostream>

#include <cstdio>

#include <cmath>

#include <cstring>

#define maxn 1000007

using namespace std;



int val[4*maxn];

char s1[maxn],s2[maxn];



void pushup(int rt)

{

    val[rt] = val[rt<<1] + val[rt<<1|1];

}

void build(int l,int r,int rt)

{

    if (l == r)

    {

        if (s1[l] == s2[l]) val[rt] = 0;//相同为0,不同为1

        else val[rt] = 1;

        return ;

    }

    int m = (l + r)>>1;

    build(l,m,rt<<1);

    build(m + 1,r,rt<<1|1);

    pushup(rt);

}

void update(int pos,char *sc,int l,int r,int rt,int mk)

{

    if (l == r)

    {

        if (mk == 1) s1[l] = sc[0];

        if (mk == 2) s2[l] = sc[0];

        if (s1[l] == s2[l])

        val[rt] = 0;

        else 

        val[rt] = 1;

        return ;

    }

    int m = (l + r)>>1;

    if (pos <= m) update(pos,sc,l,m,rt<<1,mk);

    else update(pos,sc,m + 1,r,rt<<1|1,mk);

    pushup(rt);

}

int getsum(int L,int R,int l,int r,int rt)

{

    if (l >= L && r <= R)

    {

        return val[rt];

    }

    int res = 0;

    int m = (l + r)>>1;

    if (L <= m) res += getsum(L,R,l,m,rt<<1);

    if (R > m) res += getsum(L,R,m + 1,r,rt<<1|1);

    return res;

}

int query(int pos,int l,int r,int rt)

{

    if (l == r) return l;

    int res = 0;

    int m = (l + r)>>1;

    if (pos <= val[rt<<1]) res = query(pos,l,m,rt<<1);

    else

    {

        pos -= val[rt<<1];

        res = query(pos,m + 1,r,rt<<1|1);

    }

    return res;



}

int main()

{

    int t;

    int x,y;

    char z[3];

    int cas = 1;

    scanf("%d",&t);

    while (t--)

    {

        scanf("%s%s",s1,s2);

        int len1 = strlen(s1);

        int len2 = strlen(s2);

        int len = max(len1,len2);

        build(0,len,1);

        int q,op;

        scanf("%d",&q);

        printf("Case %d:\n",cas++);

        while (q--)

        {

            scanf("%d",&op);

            if (op == 2)

            {

               scanf("%d",&x);

               if (s1[x] != s2[x])

               printf("0\n");

               else

               {

                   int ret = getsum(0,x,0,len,1);//首先找出[0,x]不同的点的个数

                   ret++;//++后就是要查找的第几个不同的点了

                   int pos = query(ret,0,len,1);//返回第ret个不同点的位置

                   printf("%d\n",pos - x);

               }

            }

            else

            {

                scanf("%d%d%s",&x,&y,z);

                update(y,z,0,len,1,x);

            }

        }

    }

    return 0;

}

你可能感兴趣的:(test)