[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;
}