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;
}
posted @ 2012-08-03 13:40  E_star  阅读(326)  评论(2编辑  收藏  举报