Codeforces Round #428 (Div. 2)

Codeforces Round #428 (Div. 2)


第一次进20…纪念一下:

ranklist


B. Game of the Rows

全场fst题强行区分….这道题让我从60+变成了17…

其实只要记录现在有几个4人座,几个2人座,几个1人座(四人座变来的),然后分配的尽量减少空座位就行了…虽然总感觉有些不对..

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 105;
int n, k;
int a[MAXN];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= k; i++) scanf("%d", &a[i]);
    int F = n, T = 2*n, O = 0;
    for (int i = 1; i <= k; i++) {
        int num = a[i]/4;
        if (F >= num) F -= num, a[i] -= 4*num;
        else a[i] -= F*4, F = 0;
        num = a[i]/2;
        if (T >= num) T -= num, a[i] -= 2*num;
        else a[i] -= 2*T, T = 0;
        if (a[i] == 0) continue;
        else if (a[i] == 1) {
            if (O) O--;
            else if (F) F--, T++;
            else if (T) T--;
            else { puts("NO"); return 0; }
        } else if (a[i] == 2) {
            if (F) F--, O++;
            else if (O >= 2) O -= 2;
            else { puts("NO"); return 0; }
        } else if (a[i] == 3) {
            if (O >= 3) O -= 3;
            else if (F) F--;
            else { puts("NO"); return 0; }
        } else if (a[i] <= O) O -= a[i];
        else { puts("NO"); return 0; }
    }
    puts("YES");
    return 0;
}

C. Journey

显然只有在叶子才会停…大力

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 100005;
struct node {
    int to, next;
} edge[MAXN*2];
int head[MAXN], top = 0;
inline void push(int i, int j)
{ edge[++top] = (node) {j, head[i]}, head[i] = top; }

double len = 0;

void dfs(int nd, int f, double p, int l)
{
    int cnt = 0;
    for (int i = head[nd]; i; i = edge[i].next) {
        if (edge[i].to == f) continue;
        cnt++;
    }
    for (int i = head[nd]; i; i = edge[i].next) {
        if (edge[i].to == f) continue;
        dfs(edge[i].to, nd, p/cnt, l+1);
    }
    if (cnt == 0) len += l*p; 
}

int n, u, v;

int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++) {
        scanf("%d%d", &u, &v);
        push(u, v), push(v, u);
    }
    dfs(1, 0, 1, 0);
    printf("%.15f\n", len);
    return 0;
}

D. Winter is here

题意:给定一个集合,求其所有子集的权值的和。集合的权值定义为:如果集合元素 gcd 为1,权值为 0 ;否则权值为集合大小乘以gcd.

f(x) x|gcd 的集合的大小总和。显而易见,这样的集合必然是所有 x 的倍数的子集。设这样的元素有n个,则集合总大小为:

kk(nk)=kn(n1k1)=nk(n1k)=n×2n1

g(x) x=gcd 的集合的大小总和,则:

g(x)=f(x)i2g(ix)

最后的答案就是:

x2xg(x)

复杂度是 O(nlnn) .

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005, mod = 1e9+7;

int a[MAXN];
int n;
int f[MAXN*5], g[MAXN*5];
int C[MAXN*5];

inline int power(int a, int n)
{
    int ans = 1;
    for (int i = 0; i <= 30; i++) {
        if (n&(1<<i)) ans = (long long)ans*a%mod;
        a = (long long)a*a%mod;
    }
    return ans;
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]), C[a[i]]++;
    for (register int i = 1; i <= 1000000; i++) {
        int cnt = 0;
        for (register int j = i; j <= 1000000; j += i)
            cnt += C[j];
        f[i] = (long long)cnt*power(2, cnt-1)%mod;
    }
    for (register int i = 1000000; i >= 1; i--) {
        g[i] = f[i];
        for (register int j = i*2; j <= 1000000; j += i)
            g[i] = (g[i]-g[j])%mod;
        (g[i] += mod) %= mod;
    }
    int tot = 0;
    for (register int i = 2; i <= 1000000; i++)
        tot = (tot+(long long)g[i]*i%mod)%mod;
    (tot += mod) %= mod;
    printf("%d\n", tot);
    return 0;
}

E. Mother of Dragons(补)

上午刚和小教室众王讨论过拉格朗日乘子法的奥义…然而不知道怎么限制 xi0

std告诉我们,最优方法一定是给一个团均分,其他的都是0…至于证明…看不懂QwQ

考虑如何求最大团。可以用meet-in-the-middle来做…设第一块大小为 S ,第二块为nS,由于第一块要枚举子集方便第二块匹配,复杂度是:

O(3S+2nS)

求导并另导数为0,解得极值点 S0.387n ,带入做就好了。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 45;

int g[MAXN][MAXN], f[MAXN], h[MAXN];
int n, k;

int st[1<<21|1];

int main()
{
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            scanf("%d", &g[i][j]);
        }
        g[i][i] = 1;
    }
    int L = max(1, int(n*0.387)), ans = 0;
    memset(f, 0, sizeof f);
    for (int i = 0; i < L; i++)
        for (int j = 0; j < L; j++)
            f[i] |= g[i][j]<<j;
    for (int i = 0; i < 1<<L; i++) {
        int sum = i, cnt = 0;
        for (int j = 0; j < L; j++)
            if (i&(1<<j))
                sum &= f[j], cnt++;
        if (sum == i) st[i] = cnt, ans = max(ans, cnt);
    }
    for (int i = 0; i < 1<<L; i++) {
        int sub = i;
        while (sub) {
            st[i] = max(st[i], st[sub]);
            sub = (sub-1)&i;
        }
    }
    memset(f, 0, sizeof f);
    for (int i = 0; i < n-L; i++) {
        for (int j = 0; j < n-L; j++) 
            f[i] |= g[i+L][j+L]<<j;
        for (int j = 0; j < L; j++)
            h[i] |= g[i+L][j]<<j;
    }
    for (int i = 0; i < 1<<(n-L); i++) {
        int sum = i, cc = (1<<L)-1, cnt = 0;
        for (int j = 0; j < n-L; j++)
            if (i&(1<<j))
                sum &= f[j], cc &= h[j], cnt++;
        if (sum == i) {
                ans = max(ans, cnt+st[cc]); 
        }
    }
    if (ans == 0) puts("0.00000");
    else 
        printf("%.10f\n", (double)k*k*(ans-1)/2/ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值