2014.7.24 - 多校2

本文详细解析了HDU在线评测系统中的三道题目,包括ZCCLovesIntersection、ZCClovescards及ZCCLovesCodefires的算法思路与实现细节。针对不同问题,文章介绍了计算几何概率、搜索最优排列组合以及贪心算法的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

hdu-4873-ZCC Loves Intersection

problem

1. 输入n[1, 1e9]和d[2, 99].

2. 表示在d维空间,每一维是[0, n-1].

3. 现在随机等概率取d个线段,分别平行于d个轴。

4. 这样就取了d个线段,每两个线段相交就有一个交点,

5. (m个线段交于一点,就是有m-1个点,从官方题解种理解到的这句话。)

6. 求交点个数期望。

think

1. 我们先考虑两个线段的情况。

他俩共面的概率是1/n^(d-2), 因为除了他俩在的两维,其他维都一样就是共面了。

现在我们这考虑这两个线段在这两维的情况就可以了。

设第一个线段是[x0, y1], [x0, y2]

设第二个线段是[x1, y0], [x2, y0]

那么他俩相交的概率就是(p(x1<=x0<=x2))^2 (因为y是相同的求法相同的概率,所以我们平方就可以了。)

p(x1<=x0<=x2) = (2*(n-1) + 3*(n-2) + …… + n*(n-(n-1)))/ (C(n, 2) * n)

   = ( 2n+3n+……nn - (1*2+2*3+……+(n-1)n) ) * 2 / nn(n-1)

   = (n+4) / 3n

2. 这是两个线段的。一共有C(d, 2)种两个线段。所以最后答案就是:

C(d, 2) * (1/n^(d-2)) * (p(x1<=x0<=x2))^2 = C(d, 2) * (n+4)^2 / (3^2 * n^d)

3. 需要高精度。

分子分为两个数,C(d, 2) * (n+4) 和 (n+4), 分母分为三部分,3的指数,n的指数,n和分子约分后剩下的数(令这个数为x)。

这样当分子的第一个数约分后,就让第二个数与x约分,这里要防止爆long long.

code

LL bit[2][111111];//用于模拟分子的高精度
LL fenmu[2];//分别表示3和n的指数
LL fenzi[2];//表示这2个数相乘
LL n, d;
int cnt[2];

void merge(int id){
    for(int j = 0; j < cnt[id]; ++j){
        bit[id][j+1] += bit[id][j] / 10LL;
        bit[id][j] %= 10LL;
    }
    while(bit[id][cnt[id]]){
        bit[id][cnt[id]+1] = bit[id][cnt[id]]/10LL;
        bit[id][cnt[id]] %= 10LL;
        ++cnt[id];
    }
}

int main(){
//	freopen("data.txt", "r", stdin);
//	freopen("a1.out","w",stdout);
    LL g;
    while(scanf("%I64d%I64d", &n, &d) != EOF){
        fenmu[0] = 2; //3的指数
        fenmu[1] = d; //n的指数
        memset(bit, 0, sizeof(bit));
        bit[0][0] = 1;
        bit[1][0] = 1;
        fenzi[0] = d * (d-1) / 2 * (n+4);
        fenzi[1] = n+4;
        cnt[0] = 1;
        cnt[1] = 1;
        for(int i = 0; i <= 1; ++i){
            while(fenmu[0] >= 1){
                g = __gcd(3LL, fenzi[i]);
                if(g == 1) break;
                fenzi[i] /= g;
                --fenmu[0];
            }
        }

        while(fenmu[1] >= 1){
            LL g = __gcd(n, fenzi[0]);
            if(g == 1) break;
            fenzi[0] /= g;
            --fenmu[1];
            LL tmp = n/g;
            LL gg = __gcd(tmp, fenzi[1]);
            fenzi[1] /= gg;
            for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= tmp/gg;
            merge(1);
        }

        while(fenmu[1] >= 1){
            LL g = __gcd(n, fenzi[1]);
            if(g == 1) break;
            fenzi[1] /= g;
            --fenmu[1];
            for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= n/g;
            merge(1);
        }

        bit[0][0] = fenzi[0];
        merge(0);
        for(int j = 0; j < cnt[0]; ++j) bit[0][j] *= fenzi[1];
        merge(0);

        for(int i = 0; i < fenmu[0]; ++i){
            for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= 3;
            merge(1);
        }
        merge(1);
        for(int i = 0; i < fenmu[1]; ++i){
            for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= n;
            merge(1);
        }
        for(int i = cnt[0]-1; i >= 0; --i) cout<<bit[0][i];
        if(!(cnt[1]==1 && bit[1][0]==1)){
            printf("/");
            for(int i = cnt[1] - 1; i >= 0; --i) cout<<bit[1][i];
        }
        puts("");
    }

    return 0;
}

hdu-4876-ZCC loves cards

problem

1. 输入n[1, 20], k[1, min(n, 6)], L[1, 100], n个ai[1, 100]

2. 给你n个数,分别是ai,从中任选k个,任意排成一个环,排完就不许变顺序了。

3. 这时,从环中任取m个连续的数,得到一个亦或和的值。由于位置和m任意,所以可以得到很多亦或和的值。

4. 在这很多的亦或和的值里面,找到包含[L, R]这个区间里面的所有整数的最大的R。如果没有输出0.

think

1. 先设R = L, 然后慢慢更新变大R.

2. 首先取k个数,这样复杂度是C(n, k).

3. O((1<<k)*k + (R-L+1)) 判断不考虑顺序是否包含[L, R]; 如果不包含,这k个数是不行的;

4. 如果包括,k!枚举顺序。然后k^2判断时候可以。

5. 总复杂度是O( C(n, k) * ((1<<k)*k + (R-L+1) + p * k! * k^2)  ) 其中p是可以执行第4步的概率。

code

int a[22];
int b[10];
int vis[130];
int op[10];
bool flag;
int n, k, L, R;

bool ok(){
    memset(vis, 0, sizeof(vis));
    for(int i = 0; i < (1<<k); ++i){
        int ans = 0;
        for(int j = 0; j < k; ++j){
            if(i & (1<<j)) ans = (ans ^ b[j]);
        }
        vis[ans] = 1;
    }
    for(int i = L; i <= R; ++i) if(!vis[i]) return false;
    return true;
}

void dfs(int i1, int i2){
    if(i2 == k){
        if(ok()){
            flag = true;
            int sum = 0;
            for(int i = 0; i < k; ++i) sum = (sum ^ b[i]);
            do{
                memset(vis, 0, sizeof(vis));
                op[0] = b[0];
                vis[b[0]] = 1;
                vis[sum^b[0]] = 1;
                for(int i = 1; i < k; ++i){
                    op[i] = (op[i-1] ^ b[i]);
                    vis[op[i]] = 1;
                    vis[sum^op[i]] = 1;
                    for(int j = 0; j < i; ++j){
                        int tmp = op[i] ^ op[j];
                        vis[tmp] = 1;
                        vis[sum ^ tmp] = 1;
                    }
                }
                for(int i = L; ; ++i){
                    if(!vis[i]){
                        R = max(R, i - 1);
                        break;
                    }
                }
            }while(next_permutation(b, b + k));
        }
    }
    for(int i = i1; i < n; ++i){
        b[i2] = a[i];
        dfs(i+1, i2+1);
    }
}

int main(){
    int T, tt = 0;
    while(scanf("%d%d%d", &n, &k, &L) != EOF){
        R = L;
        for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
        sort(a, a + n);
        flag = 0;
        dfs(0, 0);
        R = flag * R;
        printf("%d\n", R);
    }
    return 0;
}

hdu-4882-ZCC Loves Codefires

problem

n个东西。每个东西有两个值。Ei和Ti。每个东西的花费是 (他前面的Ti和+Ti)*Ei

求最小的总花费和。

think

看数据范围像贪心。

对于两个东西,设他们是Ti,Ei,Tj,Ej,前面T和是T,

i放前面是 (T + Ti)*Ei + (T + Ti + Tj)*Ej

j放前面是 (T + Ti + Tj)*Ei + (T + Tj)*Ej

就看Ti*Ej 和Tj*Ei 谁更小。

但是并不会严格证明这是对的。官方结题报告也没有给出严格证明。只是想想觉得这就是对的。

code

const int N = 111111;
struct point{
    LL E, K;
}p[N];

bool cmp(point x, point y){
    LL a = x.E*x.K + (x.E+y.E)*y.K;
    LL b = y.E*y.K + (y.E+x.E)*x.K;
    return a < b;

}

int main () {
    int n;
    while(scanf("%d", &n) != EOF){
        for(int i = 0; i < n; ++i) scanf("%I64d", &p[i].E);
        for(int i = 0; i < n; ++i) {
            scanf("%I64d", &p[i].K);
        }
        sort(p, p + n, cmp);
        LL ans = 0;
        LL T = 0;
        for(int i = 0; i < n; ++i){
            T += p[i].E;
            ans += T * p[i].K;

        }
        printf("%I64d\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值