A subsegment reverse
题目:
代码:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 2e5 + 10;
int n;
int l, r;
int a[N];
int main() {
cin >> n >> l >> r;
for(int i = 1; i <= n; i ++ ) a[i] = i;
for(int i = 1; i < l; i ++ ) cout << a[i] << " ";
for(int i = r; i >= l; i -- ) cout << a[i] << " ";
for(int i = r + 1; i <= n; i ++ ) cout << a[i] << " ";
return 0;
}
B Nutrients
题目:
思路:暴力枚举,枚举后作比较
代码:
#include <iostream>
using namespace std;
const int N = 110;
int a[N][N];
int w[N];
int n, m;
int main() {
cin >> n >> m;
for(int i = 1; i <= m; i ++ ) cin >> w[i];
for(int i = 1; i <= n; i ++ ) {
for(int j = 1; j <= m; j ++ ) {
cin >> a[i][j];
}
}
for(int i = 1; i <= m; i ++ ) {
int cnt = 0;
for(int j = 1; j <= n; j ++ ) {
cnt += a[j][i];
}
if(cnt < w[i]) {
cout << "No" << endl;
return 0;
}
}
cout << "Yes";
return 0;
}
C keys
题目:
思路:注意到n很小,因此考虑状态压缩,枚举所有情况,如果符合m个条件及该情况成立,ans ++
代码:
#include <iostream>
using namespace std;
int n, m, k;
int g[110][20], re[110];
int main() {
cin >> n >> m >> k;
for(int i = 1; i <= m; i ++ ) {
int x;
cin >> x;
while(x -- ) {
int j;
cin >> j;
g[i][j] = 1;
}
char c;
cin >> c;
re[i] = c == 'o'? 1: 0;
}
int ans = 0;
for(int i = 0; i <= (1 << n) - 1; i ++ ) {
bool ok = 1;
for(int j = 1; j <= m; j ++ ) {
int cnt = 0;
for(int u = 1; u <= 15; u ++ ) {
int x = i >> (u - 1) & 1;
if(x && g[j][u]) cnt ++;
}
if(re[j]) {
if(cnt < k) ok = 0;
} else {
if(cnt >= k) ok = 0;
}
}
if(ok) ans ++;
}
cout << ans;
return 0;
}
D masked popcount
题目:
思路:位运算,对m拆位后,对于m第i位的二进制数字x,只有x为1时对答案有贡献,于是问题变成了找到1 ~ n中所有第i位(m >> (i - 1) & 1 == 1)为1的1的总和。
由于n很大,暴力会t,因此考虑寻找某些规律
000001
000010
000011
000100
000101
000110
000111
001000
注意到从右向左第i位数字1与0会形成包含1 << i个0与1(其中01都是1 << i - 1个)的循环节,因此对每一位先计算n中包含几个循环节,再算余数(上述式子从0开始,因此n实际上是第n + 1个数)
代码:
#include <iostream>
using namespace std;
const int mod = 998244353;
typedef unsigned long long ULL;
int main() {
ULL n, m;
cin >> n >> m;
ULL ans = 0;
for(int i = 0; i <= 59; i ++ ) {
ULL x = (m >> i) & 1;
if(!x) continue;
ULL cnt = (n + 1) / (1ULL << i + 1) * (1ULL << i);
ULL backupn = n + 1;
ULL reminder = (backupn % (1ULL << i + 1) > (1ULL << i))? backupn % (1ULL << i + 1) - (1ULL << i): 0;
ans = ((ans % mod) + (cnt % mod) + (reminder % mod)) % mod;
}
cout << ans;
return 0;
}
E max/min
题目:
思路:注意到问题实际上把所有元素都做一遍比较,并且用大的数除小的数,因此最开始的思路是先sort,再用前缀和做,最后发现样例都没过去,发现前缀和会影响每个pair下去整的性质,例如3 / 2 + 3 / 2下去整 = 3 但是 s[3 + 3] / 2 = 3 并且找不到优化方法。于是注意到值域很小,考虑枚举值域,并把值域分段,x ---- 2x - 1下取整是1 2x ---- 3x - 1下取整是2 。。。。第二层循环对值域分段是一个调和级数,时间复杂度lg级别
代码:
#include <iostream>
using namespace std;
const int N = 1e6 + 10;
typedef long long LL;
int a[N], cnt[N];
LL s[N];
int main() {
int n;
cin >> n;
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
cnt[a[i]] ++;
}
// x --- 2x - 1 --> 1; 2x --- 3x - 1 --> 2
for(int i = 1; i <= 1000000; i ++ ) s[i] = s[i - 1] + cnt[i];
LL ans = 0;
for(int i = 1; i <= 1000000; i ++ ) {
if(!cnt[i]) continue;
for(int j = i; j <= 1000000; j += i) {
int l = j - 1;
int r = min(1000000, j + i - 1);
if(j == i) l = j;
ans += (LL)cnt[i] * (r / i) * (s[r] - s[l]);
}
if(cnt[i] > 1) ans += ((LL)cnt[i] * (cnt[i] - 1)) / 2;
}
cout << ans;
return 0;
}