[Offer收割]编程练习赛21

题目1 : 集合计数

题意

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定一个包含N个整数的集合S={A1, A2, ... AN},以及一个给定的整数K,请计算有多少个S的子集满足其中的最大值与最小值的和小于等于K。

例如对于S={4, 2, 5, 8}以及K=7,满足的条件的子集有以下4个:{2}, {2, 4}, {2, 5}, {2, 4, 5}。

输入
第一行包含两个整数N和K。

第二行包含N个整数A1, A2, ... AN。

对于30%的数据,1 <= N <= 20

对于70%的数据,1 <= N <= 1000

对于100%的数据,1 <= N <= 100000, 0 <= Ai <= 1000000000, 0 <= K <= 2000000000, A1 ~ AN 两两不同。

输出
输出满足条件的子集数目。由于答案可能非常大,你只需要输出答案除以1000000007的余数。

样例输入
4 7
4 2 5 8
样例输出
4

题解

对于a[i],另a[i]+a[j]<=k, a[i]=a[j-1]>k.则以a[i]为最小值并满足题意的子集个数为2^(j-i) (a[i+1] 到a[j]选和不选两种选择),排序后二分即可。 2^(j-i)可使用快速幂或提前打表。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1000000007;
int n;
ll k;
ll a[100005];
ll quick_power(ll a, int b) {
    ll ans = 1;
    while(b) {
        if (b&1) {
            ans = ans * a % mod;
        }
        b /= 2;
        a = a *a%mod;
    }
    return ans;
}
int main() {
    cin >> n >> k;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    sort(a, a+n);
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        int index = upper_bound(a+i, a+n, k-a[i]) -(a+i)-1;
        if(index>=0)
            ans = (ans + quick_power(2, index)) % mod;
            if(ans<0) ans = ans + mod;
    }
    cout << ans << "\n";
    return 0;
}

题目2 : 迷宫探索

题意

间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
小Hi要探索一间NxM大小的迷宫。下图展示了一间大小为2x3的迷宫:

1.png

其中黑色线表示墙壁,无法通过。

迷宫一定有且只有一个入口,在左上角的上方。

小Hi从左上角进入迷宫并对迷宫进行探索,保证将迷宫的每一个房间都走一遍然后退出迷宫。

在这个过程中小Hi会记录每一步四周墙壁的情况和下一步移动的方向。

最开始时,小Hi处于左上角,并且面向正下方。最后他会从左上角的房间离开迷宫。

请你根据记录还原出迷宫的地图。

为了方便起见,我们将迷宫使用ASCII码字符进行表示,比如上例中2x3的迷宫表示为:

  • +-+-+
    | | |
    +-+ + +
    | |
    +-+-+-+

其中'+', '|', '-'表示墙壁,其他部分为空格。

输入
第1行:2个正整数,N, M表示迷宫长宽,1<=N, M<=200

接下来2NM-1行记录:每行3个整数l, f, r和1个字符d。

l, f, r表示当前位置以面向为准,左方(left),正前方(front),右方(right)是否有墙壁,有墙壁为1,没有则为0。

d表示下一步移动的绝对方向,'l','r','u','d'分别表示向左、向右、向上、向下,移动之后的面向和移动方向相同。比如向上移动一格之后,则面向正上方。

输出
以ASCII码绘制的地图,注意左上角的上方一定为空格。

样例说明
(1,1) -> (1,2) -> (2,2) -> (2,1) -> (2,2) -> (2,3) -> (1,3) -> (2,3) -> (2,2) -> (1,2) -> (1,1) -> exit

    •  +-+      + +      +-+      + +      + +      +-+      + +      + +      +-+      + +   
      

|v >| v |< > >| |^| v| < ^| |<
+-+ + + +-+ +-+ +-+ +-+ + + +-+ +-+ + + +-+
样例输入
2 3
0 1 1 r
1 1 0 d
0 1 0 l
1 1 1 r
0 0 1 r
0 1 1 u
1 1 1 d
1 1 0 l
1 0 0 u
0 1 1 l
1 1 0 u
样例输出

  • +-+-+
    | | |
    +-+ + +
    | |
    +-+-+-+

直接模拟即可。

题目3 : 召唤佣兽

题意

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
死灵飞龙维萨吉可以召唤2~3只佣兽来帮助他追杀他的敌人,“死灵飞龙的佣兽,也是狭窄迷宫的守卫,俯视着他的领地”。

战场可以被看做一个N*M的矩形,其中左上角的坐标为(0, 0), 右下角的坐标为(N, M),死灵飞龙以及他的佣兽们的位置分别为(Xi, Yi)。

而他们追杀的敌人的位置为(Xs, Ys),这名敌人只需要逃到(Xt, Yt)就可以逃离死灵飞龙的追杀。

逃跑的过程中,一旦敌人的坐标与死灵飞龙或者佣兽的曼哈顿距离(横纵坐标的差之和)小于K,那么死灵飞龙就会发现敌人的踪迹,从而发起攻击。

现在维萨吉想要知道,他组织的监视网是否存在漏洞,从而让敌人逃脱。注意敌人可以在矩形范围内任意移动,并非只能沿着坐标轴在整点上移动。

输入
第一行为一个整数T,表示测试数据的组数。

每个测试数据的第一行为四个整数N、M、K、Q,分别表示战场的大小,死灵飞龙(以及佣兽)的视野距离以及死灵飞龙的佣兽数量。

接下来的Q+1行,每行一个坐标(Xi, Yi),分别描述死灵分龙和它佣兽们的坐标,死灵飞龙和它的佣兽们可能处于同一位置。

最后两行为四个整数Xs, Ys和Xt, Yt,分别表示敌人的起始位置和脱离位置。

起始位置和脱离位置可能直接处于死灵飞龙的监视网范围内,这种情况下敌人无法逃脱。

对于30%的数据,满足1<=N, M, K<=10

对于100%的数据,满足

1<=N*M<=108, 1<=K<=108, 0<=Xi,Xs,Xt<=N, 0<=Yi,Ys,Yt<=M, 2<=Q<=3, T<=10

输出
对于每组测试数据,如果敌人能够逃脱死灵飞龙的追杀,输出“Yes”,否则输出“No”。

样例输入
2
10 7 4 2
7 4
1 5
5 6
3 3
5 7
9 9 3 3
0 4
2 8
1 5
4 4
9 0
9 6
样例输出
No
Yes

题解

不会。。。

题目4 : 或运算和

题意

时间限制:10000ms
单点时限:1000ms
内存限制:256MB
描述
给定N个数A1...AN (0 <= Ai < 220) 和一个正整数K,我们用Ans[i]表示有多少种从这N个数中选取K个数的方案,满足这K个数的或运算和正好为i。

你能对于每一个i(0 <= i < 220)都计算出Ans[i]的值吗?

为了简化输出,你只需要输出Σ(Ans[i] * i) 除以1000000007的余数。

输入
第一行一个数T(<=10),表示数据组数

对于每一组数据:

第一行两个数N,K(1<=N<=100,000,K<=1000)

第二行N个数A1...AN (0 <= Ai < 220)

输出
一个数表示答案

样例输入
2
3 2
1 2 3
4 2
1 2 4 5
样例输出
9
31

题解

对Σ(ans[i] * i) 根据i的二进制表示进行拆解,另i = Σ2^j;(j为二进制中为一的位)。则\(\sum_i \left({ans[i] * \sum_j 2^j }\right) = \sum_j \left( {2^j * \sum_{i, i \& 2^j = 1} ans[i]}\right)\)

\(\sum_{i, i \& 2^j = 1} {ans[i]}\)就是从n个数中选k个数使得第j位为一的种类数。也就是转化为枚举每一位对结果的贡献。复杂度为O(20n + 20k*log(1e9)) = O(n)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int a[100005];
int cnt[25];
const int mod = 1000000007;
ll quick_mod(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) {
            ans = ans * a % mod;
        }
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}
ll c(ll m, ll n) {
    ll cm = 1, cn = 1;
    for (int i = 0; i < n; i++) {
        cn = cn * (n-i) % mod;
        cm = cm * (m-i) % mod;
    }
    return cm * quick_mod(cn, mod-2) % mod;
} 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) {
        int n, k;
        cin >> n >> k;
        memset(cnt, 0, sizeof(cnt));
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            for (int j = 0; j < 25;j++) {
                if (a[i] & (1<<j)) cnt[j]++;
            }
        }
        ll ans = 0;
        ll sum = c(n, k);
        ll p2 = 1;
        for (int i = 0; i <= 20; i++) {
            ans = (ans + (sum - c(n-cnt[i], k) + mod) % mod * p2) % mod;
            p2 = p2 * 2 % mod;
        }
        cout << ans << "\n";
    }
    return 0;
}
posted @ 2017-08-07 21:23  hxidkd  阅读(374)  评论(0编辑  收藏  举报