A-Bit Common
题意
给定两个整数 n n n 和 m m m,在所有包含 n n n 个非负整数且每个整数小于 2 m 2^m 2m 的序列中,你需要统计其中有多少个序列 A A A 存在一个非空的子序列,其所有元素按位与的结果为 1。
注意,序列 A A A 的非空子序列是可以通过删除 A A A 的零个或多个元素并按原顺序排列剩余元素得到的非空序列。
由于答案可能非常大,请输出对一个正整数 q q q 取模的结果。
思路
思维。为了使非空子序列的 A N D AND AND 和为 1 1 1 ,子序列中的数的二进制最低位必须为 1 1 1 。可以枚举子序列的个数,保证子序列之外的数的二进制最低位为 0 0 0,枚举这些数。对于二进制最低位为 1 1 1 的数,设此时有 i i i 个,为了这些数的 A N D AND AND 和为 0 0 0,要保证这些数最低位的位置全为 1 1 1 ,其他 m − 1 m-1 m−1 个位置上,每一个位置至少有一个 0 0 0,对于这部分的数,此时的情况和为 ( 2 i − 1 − 1 ) m − 1 {(2^{i - 1} - 1)}^{m - 1} (2i−1−1)m−1;对于其他二进制最低位为 0 0 0 的 n − i n - i n−i 个数,其他 m − 1 m - 1 m−1 个位置随意,情况数为 2 ( n − i ) ( m − 1 ) 2^{(n - i)(m - 1)} 2(n−i)(m−1)。对于一个 i i i,总情况数为 C n i × 2 ( n − i ) ( m − 1 ) × ( 2 i − 1 − 1 ) m − 1 C_n^i \times2^{(n - i)(m - 1)} \times {(2^{i - 1} - 1)}^{m - 1} Cni×2(n−i)(m−1)×(2i−1−1)m−1,最终答案 a n s = ∑ i = 1 n C n i × 2 ( n − i ) ( m − 1 ) × ( 2 i − 1 − 1 ) m − 1 ans=\sum_{i=1}^nC_n^i \times2^{(n - i)(m - 1)} \times {(2^{i - 1} - 1)}^{m - 1} ans=∑i=1nCni×2(n−i)(m−1)×(2i−1−1)m−1。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 5e3 + 3;
int C[N][N];
int qpow(int a, int n, int mod) {
int ans = 1;
while(n) {
if(n & 1) ans = ans * a % mod;
a = a * a % mod;
n >>= 1;
}
return ans % mod;
}
void solve() {
int n, m, q;
cin >> n >> m >> q;
C[0][0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 0; j <= i; j++){
if(j == 0){
C[i][j] = 1 % q;
}
else{
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % q;
}
}
}
int ans = 0;
for(int i = 1; i <= n; i++) {
ans = (ans + C[n][i] * qpow(2, (n - i) * (m - 1), q) % q * qpow((qpow(2, i, q) - 1) % q, m - 1, q) % q) % q;
}
cout << ans << endl;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0);
int T = 1;
//cin >> T;
while(T--) {
solve();
}
return 0;
}
C-Sum of Suffix Sums
题意
给定一个初始为空的数组,需要执行
q
q
q 次操作:
对于给定的两个非负整数
t
t
t 和
v
v
v,从数组末尾连续取出
t
t
t 次元素,然后将
v
v
v 添加到数组的末尾。保证在此操作之前,
t
t
t 不会超过数组的长度。
在每次操作后,设 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,…,an 是当前数组,找到 s 1 , s 2 , … , s n s_1, s_2, \ldots, s_n s1,s2,…,sn 的和,其中 s i = a i + a i + 1 + … + a n s_i = a_i + a_{i+1} + \ldots + a_n si=ai+ai+1+…+an 是从位置 $ i $ 开始的后缀的和。
输出它们模 1 , 000 , 000 , 007 1,000,000,007 1,000,000,007 的结果。
思路
用栈模拟。对于一对 t t t 和 v v v,记此时取出的元素为 x x x,为本次操作取出的第 i d x idx idx个元素,栈内元素的个数为 c n t cnt cnt,所有元素的后缀和为 a n s ans ans,取出这个元素对于 a n s ans ans的贡献为 − x ∗ ( c n t − i d x ) -x * (cnt - idx) −x∗(cnt−idx);取出 t t t 个元素后并加入一个元素的元素个数为 c n t − t + 1 cnt - t + 1 cnt−t+1,加入元素 v v v 对答案的贡献为 + c n t ∗ v + cnt * v +cnt∗v。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int mod = 1e9 + 7;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int q = 1, ans = 0, cnt = 0;
cin >> q;
stack<int> s;
while(q--) {
int t, v;
cin >> t >> v;
for(int i = 0; i < t; i++) {
int pos = s.top();
s.pop();
ans -= (cnt - i) * pos;
ans = (ans % mod + mod) % mod;
}
cnt++;
cnt -= t;
s.push(v);
ans = (ans + v * cnt) % mod;
cout << ans << endl;
}
return 0;
}
H-World Finals
题意
l z r 010506 lzr010506 lzr010506 拥有参加两场比赛的资格,但是只能参加一场比赛。其他学校的中的部分也拥有参加两场比赛的资格,但是 l z r 010506 lzr010506 lzr010506 可以替他们决定参加哪场比赛。问 l z r 010506 lzr010506 lzr010506 能拿到的最高名次是多少。
比赛排名的依据为过题数量和罚时,过题越多名次越靠前;相同过题数罚时越少名次越靠前。
思路
模拟 + + + 贪心。得到正确的比赛排名后,在一场比赛中,若有参加两场比赛资格的学校名次在 l z r 010506 lzr010506 lzr010506 前,则把此学校移到另外一场比赛。答案取最小值。
代码
#include "bits/stdc++.h"
#define int long long
#define endl '\n'
using namespace std;
struct Team {
string s;
int num, pen;
};
bool cmp(const Team& a, const Team& b) {
if (a.num == b.num)
return a.pen < b.pen;
return a.num > b.num;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
vector<Team> v1(n);
for (int i = 0; i < n; ++i) {
cin >> v1[i].s >> v1[i].num >> v1[i].pen;
}
sort(v1.begin(), v1.end(), cmp);
int m;
cin >> m;
vector<Team> v2(m);
for (int i = 0; i < m; ++i) {
cin >> v2[i].s >> v2[i].num >> v2[i].pen;
}
sort(v2.begin(), v2.end(), cmp);
int ans1 = 1e10, ans2 = 1e10;
set<string> cnt;
for (int i = 0; i < n; ++i) {
if (v1[i].s == "lzr010506") {
ans1 = i + 1;
break;
} else {
cnt.insert(v1[i].s);
}
}
for (int i = 0; i < m; ++i) {
if (cnt.count(v2[i].s)) {
--ans1;
}
}
cnt.clear();
for (int i = 0; i < m; ++i) {
if (v2[i].s == "lzr010506") {
ans2 = i + 1;
break;
} else {
cnt.insert(v2[i].s);
}
}
for (int i = 0; i < n; ++i) {
if (cnt.count(v1[i].s)) {
--ans2;
}
}
int ans = min(ans1, ans2);
cout << ans << endl;
return 0;
}
I-Mirror Maze
题意
有一个 n × m n \times m n×m 的镜子迷宫,每个格子里都有一面镜子。这些镜子有以下四种类型之一:
-
,从上方或下方来的光会被反射回去,从左边或右边来的光会继续向前,不会被反射。|
,从左边或右边来的光会被反射回去,从上方或下方来的光会继续向前,不会被反射。/
,从左、右、上、下来的光分别会被反射到上、下、左、右。\
,从左、右、上、下来的光分别会被反射到下、上、右、左。
现在有 q q q 个光源。对于每个光源,发出的光在足够长的时间内会被多少个不同的镜子反射。
思路
模拟 + + + 记忆化搜索。可以抽象成找环和链,每一个环和链都是唯一的。记忆化搜索环,链不用记忆化搜索。用数字来表示不同状态: d x i dx_i dxi和 d y i dy_i dyi ( 0 ⩽ i ⩽ 3 ) (0 \leqslant i \leqslant 3) (0⩽i⩽3) 表示光线坐标移动状态; a n s i j k ans_{ijk} ansijk表示坐标为 ( i , j ) (i,j) (i,j)的镜子第 k k k种状态的答案; v i s i j k vis_{ijk} visijk记录一次 d f s dfs dfs搜索到的点。具体见代码注释。
代码
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
const int N = 1e3 + 3;
// mp为镜子阵 ans维护镜子阵每个点不同方向射入的结果 cir记录是否成环 vis记录节点是否被遍历
int mp[N][N], ans[N][N][4], cir[N][N][4], vis[N][N][4];
// 0 <-> '|' 1 <-> '-' 2 <-> '/' 3 <-> '\'
int to[4][4]={0, 1, 3, 2, 1, 0, 2, 3, 3, 2, 1, 0, 2, 3, 0, 1};
// 0 <-> above 1 <-> below 2 <-> left 3 <-> right
int dx[4]={-1, 1, 0, 0};
int dy[4]={0, 0, -1, 1};
int n, m, q;
int dfs(int x, int y, int dir, int cnt) {
if(x < 1 || x > n || y < 1 || y > m) {
return 0;
}
if(vis[x][y][dir]) {
cir[x][y][dir] = 1;
return cnt;
}
if(cir[x][y][dir]) {
return ans[x][y][dir];
}
int tow = to[mp[x][y]][dir];
int f = 1;
for(int i = 0; i < 4; i++) {
if(vis[x][y][i]) {
f = 0;
break;
}
}
if(f && tow != dir) {
f = 1;
} else {
f = 0;
}
if(tow != dir) vis[x][y][dir] = 1;
int xx = x + dx[tow], yy = y + dy[tow], towl = dfs(xx, yy, tow, cnt + f);
if(tow != dir) vis[x][y][dir] = 0;
if(cir[xx][yy][tow]) {
ans[x][y][dir] = towl;
cir[x][y][dir] = 1;
}
else ans[x][y][dir] = towl + f;
return ans[x][y][dir];
}
void solve() {
cin >> n >> m;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
char x;
cin >> x;
if(x == '|') {
mp[i][j] = 0;
} else if(x == '-') {
mp[i][j] = 1;
} else if(x == '/') {
mp[i][j] = 2;
} else {
mp[i][j] = 3;
}
}
}
cin >> q;
for(int i = 1; i <= q; i++) {
int x, y, dir;
string s;
cin >> x >> y >> s;
if(s[0] == 'a') {
dir = 0;
} else if(s[0] == 'b') {
dir = 1;
} else if(s[0] == 'l') {
dir = 2;
} else {
dir = 3;
}
cout << dfs(x + dx[dir], y + dy[dir], dir, 0) << endl;
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T = 1;
//cin >> T;
while(T--) {
solve();
}
return 0;
}