2012 Multi-University Training Contest 4

1004 线性查找 O(n^3)

题意:有五个集合,每个集合中最多有200个数,从每个集合中选出一个数判断这五个数的和能否是零。

思路:如果直接暴力的话复杂度是O(n^5) TLE , 在网上看到一种所谓的线性查找的方法,刚开始跟这个思路差不多,但是还是TLE。。

把第一个集合的第二个集合的所有和的情况组成新的s1,第三个和第四个集合所有和的情况组成s2,然后对s1和s2分别排序  O(n^2 + n^n + nlgn + nlgn)

然后枚举最后一个集合的元素,对每一个元素,s1和s2一个从小枚举,一个从大枚举,如果s1[i] + s2[j] + tmp > 0 说明大的太大,j--; 如果小于零那么小的太小i++;这样最坏也就是2*n^n^n O(n^3) 

参考于楼长:http://www.cnblogs.com/louzhang/archive/2012/08/02/2620588.html

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
//typedef long long ll;
const int maxn = 210;
__int64 s1[maxn*maxn] , s2[maxn*maxn];
__int64 a[maxn] , b[maxn] , c[maxn] , d[maxn] , e[maxn];
int main() {
    int T , cas = 1;
    int n , i , j;
    scanf("%d",&T);
    while(T--) {
        memset(s1 , 0 , sizeof(s1));
        memset(s2 , 0 , sizeof(s2));
        scanf("%d",&n);
        for(i = 1 ; i <= n ; i ++)
        scanf("%I64d",&a[i]);
        for(i = 1 ; i <= n ; i ++)
        scanf("%I64d",&b[i]);
        for(i = 1 ; i <= n ; i ++)
        scanf("%I64d",&c[i]);
        for(i = 1 ; i <= n ; i ++)
        scanf("%I64d",&d[i]);
        for(i = 1 ; i <= n ; i ++)
        scanf("%I64d",&e[i]);
        int k = 0;
        for(i = 1 ; i <= n ; i ++) 
            for(j = 1 ; j <= n ; j ++)
            s1[k++] = a[i] + b[j];
        int t = 0;
        for(i = 1 ; i <= n ; i ++)
            for(j = 1 ; j <= n ; j ++)
            s2[t++] = c[i] + d[j];
            sort(s1 , s1 + k);
            sort(s2 , s2 + t);
            sort(&e[1], e + n + 1);
        int flag = 0;
        int f;
        for(f = 1 ; f <= n ; f ++) {
            __int64 tmp = e[f];    //没有声明__in64  WA了好几次 
            i = 0 , j = t - 1;
            while(i < k && j >= 0) {
                if(s1[i] + s2[j] + tmp == 0 ) {
                    flag = 1;
                    break;    
                }
                if(s1[i] + s2[j] + tmp > 0) j--;
                else i ++;   
            }
            if(flag) break;
        }
        if(flag) printf("Yes\n");
        else printf("No\n");
    }    
}

1006 容斥原理

因为不想浪费那么最理想的情况就是s1∪s2∪s3...∪sn   ,   si为在pi概率下要买的方便面袋数。

|A1∪A2∪...∪An| = Σ|Ai| - Σi~nΣj>i|Ai∩Aj| + (-1)^(n-1)|A1∩A2∩...∩An|

所以官方这个公式也就很裸了


还有就是求"C(n,2) C(n,3) ... C(n.n)" 是用dfs实现的

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
double s , P[25];
int k , n;
void dfs(int cnt , double p , int i) {
    if(cnt == k) {
        s += 1.0/p;
        return;
    }   
    for(int j = i ; j <= n ; j ++) {
            dfs(cnt + 1 , p + P[j] , j + 1);
    }
}
int main() {
    int i;
    while(~scanf("%d",&n)) {
        double sum = 0;
        for(i = 1 ; i <= n ; i ++) {
            scanf("%lf",&P[i]);
            sum += 1.0/P[i];
        }
        for(i = 2 ; i <= n ; i ++) {
            k = i;
            s = 0;
            dfs(0 , 0 , 1);
            if(i%2==0) s = (-1)*s;
            sum += s;    
        }
        printf("%lf\n",sum);
    }    
}

1009 线段树

题意:给出两个字符串,更新操作是更新1 a i c 代表把第a个string的第i号字符更新成字符c,询问操作是2 i代表从第一个字符串i开始去逐位匹配第二个字符串输出第一个失配的位置。

思路:很明显的线段树。。。有思路,还是搞不定这个线段树啊。。搓!后来看了下别人的思路搞了搞,可是有些细节还是处理不好。。。

主要参考:http://www.cnblogs.com/372465774y/archive/2012/08/04/2622549.html

主要就是不明白一点,在build和update 于PushUP结合的时候更新已经完成,询问的时候直接找cnt的值就完了呗,但是还是要判断在左区间的公共子串值是不是填满了左区间长度。。。不加就WA。。。 有点疑惑

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define lson l , m  , rt<<1
#define rson m + 1 , r , rt<<1|1
const int maxn = 1000010;
int cnt[maxn<<2];  // 以区间左端点为起点的公共子串长度 
char s1[maxn] , s2[maxn];
void PushUP(int rt , int m , int l) {
    //printf("Up %d\n",rt);
    if(cnt[rt<<1] == (m - l + 1)) 
        cnt[rt] = cnt[rt<<1] + cnt[rt<<1|1];
    else cnt[rt] = cnt[rt<<1];
}
void build(int l , int r , int rt) {
    if(l == r) {
        if(s1[l] == s2[l]) {
            cnt[rt] = 1;
            }
        else cnt[rt] = 0;      
        return;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    PushUP(rt , m , l);    
}
void Update(int i , int l , int r , int rt) {
    if(l == r) {
        if(s1[l] == s2[l]) cnt[rt] = 1;
        else cnt[rt] = 0;
        return;    
    }
    int m = (l + r) >> 1;
    if(i <= m) Update(i , lson);
    else Update(i , rson);
    PushUP(rt , m , l);
}
int Query(int i , int l , int r , int rt) {
    if(i == l) return cnt[rt];
    int m = (l + r) >> 1;
    int ans;
    if(i <= m) { 
        ans = Query(i , lson);
        if(ans == m - i + 1) ans += cnt[rt<<1|1]; //感觉不用加 但是去掉就WA 
    }
    else ans = Query(i , rson);
    return ans;    
}
int main() {
    int T , cas = 1;
    scanf("%d",&T);
    while(T--) {
        scanf("%s %s",s1,s2);
        int Q;
        int l1 , l2;
        l1 = strlen(s1);
        l2 = strlen(s2);
        build(0 , l1 - 1, 1);
        printf("Case %d:\n",cas++);
        scanf("%d",&Q);  
        while(Q--) {
            int t , i , j;
            char c;
            scanf("%d",&t);
            if(t == 2) {
                scanf("%d",&i);
                if(i >= l1) printf("0\n");
                else printf("%d\n",Query(i , 0 , l1 - 1, 1));    
            }
            if(t == 1) {
                scanf("%d %d %c",&i,&j,&c);
                if(i == 1) s1[j] = c;
                else s2[j] = c;
                Update(j , 0 , l1 - 1 , 1); 
            }     
        }
    }    
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值