组队赛5 - 2014.8.10

http://vjudge.net/contest/view.action?cid=53221#overview

UVALive 6434 - Number Assignment

problem

给你n个数,分为m组。每组的最大值减去最小值的差值加起来,最小是多少。

think

排序后。有n-1个差值。

答案就是这n-1的差值和(也是这n个数的最大值减最小值),减去m-1个最大的差值。


UVALive 6435 - Network Packet Ordering

problem

给你n,m,d,n个数表示A里面n个人到达的时间,m个数表示B里面m个人到达的时间。

如A里面第i个人的时间是ai,则他在 [ai, ai + d) 的时间到达。同理bi 。

A、B里面的人给的时间都是从小到大的,ai  < a(i+1) , 同理bi 。

在一个序列里面的人是依次到达,不能交换顺序。如d = 2. ai = 1, a(i+1) = 2, 也必须i在i+1前面。同理B.

AB没有顺序关系。问有多少种顺序。

think

A把时间分为n+1个区间。

由于a<a(i+1),d<=100, 所以每个bi最多在200个区间。

dp[i][j]表示bi在它可以在的第j个区间。

转移就是:

jjj = min(R[i-1], jj) - L[i-1];
dp[i][j] = sum[i-1][jjj];

其中jj表示j表示的那个区间。R[i]表示i可以在的最后那个区间,sum[i][j] = sum(dp[i][k], k <= j)

code

const int N = 50010;
const int mod = 1000000009;

int a[N], b[N];
int L[N], R[N];
int dp[N][300];
int sum[N][300];
int main(){
    int T, tt = 0;
    scanf("%d", &T);
    while(T--){
        int n, m, d;
        scanf("%d%d%d", &n, &m, &d);
        for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for(int j = 0; j < m; ++j) scanf("%d", &b[j]);
        for(int j = 0; j < m; ++j){
            int i = 0;
            if(j > 0) i = L[j-1];
            L[j] = -1;
            R[j] = -1;
            for(; i < n; ++i){
                if(b[j] >= a[i] + d) continue;
                if(i > 0 && b[j] + d <= a[i-1]) break;
                if(L[j] == -1) L[j] = i;
                R[j] = i;
            }
            if(L[j] == -1) L[j] = n;
            if(R[j] == -1 || b[j] + d > a[n-1]) R[j] = n;
        }
        for(int i = 0; i < m; ++i){
            for(int j = 0, jj = L[i]; jj <= R[i]; ++j, ++jj){
                if(i == 0){
                    dp[i][j] = 1;
                    continue;
                }
                int jjj = min(R[i-1], jj) - L[i-1];
                dp[i][j] = sum[i-1][jjj];
            }
            sum[i][0] = dp[i][0];
            for(int j = 1, jj = L[i] + 1; jj <= R[i]; ++j, ++jj){
                sum[i][j] = sum[i][j-1] + dp[i][j];
                if(sum[i][j] >= mod) sum[i][j] -= mod;
            }
        }
        printf("Case #%d: %d\n", ++tt, sum[m-1][R[m-1]-L[m-1]]);
    }
    return 0;
}

UVALive 6436 - The Busiest City

problem

一棵树。每个点的值是这个数的任两个点path经过这个点(并且这个点不是端点)的个数。

think

设s为这个点。sz[s]表示s的子树大小。ss是s的孩子。

那么 val[s] = sum(sz[ss] * (n - 1 - sz[ss]) + (n - sz[s]) * (sz[s] - 1));

code

vector<int>v[20010];
int sz[20010];
int ans, n;

void dfs(int s, int pre){
    int len = v[s].size();
    sz[s] = 1;
    int res = 0;
    for(int i = 0; i < len; ++i){
        int ss = v[s][i];
        if(ss == pre) continue;
        dfs(ss, s);
        res += sz[ss] * (n - 1 - sz[ss]);
        sz[s] += sz[ss];
    }
    res += (n - sz[s]) * (sz[s] - 1);
    if(res > ans) ans = res;
}

int main(){
    int T, tt = 0;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i = 1; i < n; i++){
            int a, b;
            scanf("%d%d", &a, &b);
            v[a].PB(b);
            v[b].PB(a);
        }
        ans = 0;
        dfs(1, 0);
        printf("Case #%d: %d\n", ++tt, ans>>1);
        for(int i = 1; i <= n; ++i) v[i].clear();
    }
    return 0;
}

UVALive 6439 - Pasti Pas!

problem

给你一个串,可以把某些字符变成一个字符,如ABCDAB可以把AB变成a,把CD变成b,这样就变成了aba。

变完一定得是回文串。

问变完最长是多长。

think

从两头找。

如果两头找到已经可以变成一样的,那么他俩一定匹配。如AB……AB,那么AB一定变成一个字符。

因为如果他俩不变的话,也一定是AB*AB 和AB*AB为了是他俩一样。

根据这个性质,就从两边找吧。而且左边的指针i和j的话,j一直往后,找到可以的j,i和j都变成j+1,所以复杂度就是串长。

然后判断的时候hash。就可以O(1)判断了。


UVALive 6440 - Emergency Handling

problem

有n个操作。P表示进来一个病人,t0,s0,r,表示在t0时刻进来,他的危险值是s0 + r * (t - t0),t 是 >= t0的时刻。

A表示t0时刻可以抢救一个人。抢救那个危险值最大的。如果危险值一样就抢救r最大的。输出抢救的人的危险值和r。

think

r是[0, 100],所以建立101个优先队列。

把病人的危险值变为s0 - r*t0 + r*t, 优先队列里面放的是s0 - r*t0.

这样进去病人的时候,复杂度是log

抢救的时候要遍历101个队列,top操作是O(1),复杂度是101.

所以总的复杂度是T * n * max(logn, 101).

code

int main(){
    int t, tt = 0;
    scanf("%d",&t);
    while(t--){
        printf("Case #%d:\n", ++tt);
        priority_queue<int,vector<int>,less<int> >q[101];
        int n;
        scanf("%d",&n);
        while(n--){
            char str[5];
            int t0,s0,r,s;
            scanf("%s%d", str, &t0);
            if(str[0] == 'A'){
                r = 0;
                LL sc = 0;
                for(int i = 0; i <= 100; ++i)
                if(!q[i].empty()){
                    s = q[i].top();
                    LL tmp = (LL)s + i * t0;
                    if(tmp >= sc){
                        r = i;
                        sc = tmp;
                    }
                }
                q[r].pop();
                printf("%lld %d\n", sc, r);
            } else {
                scanf("%d%d", &s0, &r);
                q[r].push(s0 - r*t0);
            }
        }
    }
    return 0;
}

UVALive 6441 - Horrible Quiz

problem

一个人的开始分数是15000.做n道题。他自己做对的概率是w/100, 做错的概率是c/100,使用你给的答案的概率是1 - w/100 - c/100。

你要给他n个答案,最多给m个错误答案。

作对了就乘以1,做错了就乘以-1.

求他的最低分数的期望。

think

dp[i][j][2], 分别表示到第i道题已经给了j个错误答案的期望的最大值和最小值。

做对的话就是乘以 ww = (100 - 2 * c[i+1]) / 100.;  做错就是乘以 cc = (2 * w[i+1] - 100) / 100.;

code

int w[N], c[N];
double f[N][N][2];
int n, m;

int main(){
    int T, tt = 0;
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
        for(int i = 1; i <= n; ++i) scanf("%d", &c[i]);
        for(int i = 0; i <= n; ++i) for(int j = 0; j <= m; ++j){
            f[i][j][0] = 15000;
            f[i][j][1] = -15000;
        }
        f[0][0][0] = 15000;
        f[0][0][1] = 15000;
        for(int i = 0; i < n; ++i){
            for(int j = 0; j <= i && j <= m; ++j){
                double ww = (100 - 2 * c[i+1]) / 100.;
                double cc = (2 * w[i+1] - 100) / 100.;
                f[i+1][j][0] = min(f[i+1][j][0], f[i][j][0] * ww);
                f[i+1][j][0] = min(f[i+1][j][0], f[i][j][1] * ww);
                f[i+1][j][1] = max(f[i+1][j][1], f[i][j][0] * ww);
                f[i+1][j][1] = max(f[i+1][j][1], f[i][j][1] * ww);
                f[i+1][j+1][0] = min(f[i+1][j+1][0], f[i][j][0] * cc);
                f[i+1][j+1][0] = min(f[i+1][j+1][0], f[i][j][1] * cc);
                f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i][j][0] * cc);
                f[i+1][j+1][1] = max(f[i+1][j+1][1], f[i][j][1] * cc);
            }
        }
        double ans = 15000;
        for(int j = 0; j <= m; ++j) ans = min(ans, f[n][j][0]);
        printf("Case #%d: %.3f\n", ++tt, ans);
    }
    return 0;
}

UVALive 6442 - Coins on a Ring

problem

一个环,n个位置,有m个人,m被n整除。现在要把m个人移动位置使他们的间距相同。

消耗的能量是移动的最多的那个人的移动了多少。

求最少消耗的能量。

think

排序后让他们依次对应某个可以的序列,如0, n/m, 2*n/m ……

然后依次得到差值,最大差值和最小差值的中间的数就是答案。。

code

int a[20100];

int main(){
    int T, tt = 0;
    scanf("%d", &T);
    while(T--){
        int n, m, k;
        scanf("%d%d", &n, &m);
        k = n / m;
        int mi = n;
        int mx = -n;
        for(int i = 0; i < m; ++i) scanf("%d", &a[i]);
        sort(a, a + m);
        for(int i = 0; i < m; ++i){
            int pos = k * i - a[i];
            mi = min(mi, pos);
            mx = max(mx, pos);
        }
        printf("Case #%d: %d\n", ++tt, (mx - mi + 1) / 2);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值