Codeforces Round #767 (Div. 2)
Codeforces Round #767 (Div. 2)
A. Download More RAM
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 110;
struct node {
ll a, b;
};
node mp[N];
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
ll n, k;
cin >> n >> k;
for (ll i = 1; i <= n; ++i) {
cin >> mp[i].a;
}
for (ll i = 1; i <= n; ++i) {
cin >> mp[i].b;
}
sort(mp + 1, mp + n + 1, [&](node x, node y) {
return x.a < y.a;
});
for (ll i = 1; i <= n; ++i) {
if (mp[i].a <= k) {
k += mp[i].b;
}
}
cout << k << endl;
}
return 0;
}
B. GCD Arrays
题意:给出l和r表示有从l到r这些数,一次操作可以选择两个数取出然后将他们的乘积放回去,问能否在k次操作内使得剩余数的gcd大于1
使得gcd包含2将所有的奇数和任意一个偶数合并的操作数为奇数的个数
判断k是否大于等于奇数的个数
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
int l, r, k;
cin >> l >> r >> k;
int num = (r - l + 1)/ 2;
if((l & 1) && (r & 1)) num ++;
if(k >= num) {
cout << "YES" << endl;
continue;
}
if(r == l && r > 1) {
cout << "YES" << endl;
continue;
}
cout <<"NO" << endl;
}
return 0;
}
C. Meximum Array
题意:给一个数组a,每次可以从头选择若干连续的数字从a中删去,将他们的mex值加入到b的末尾,要求使得b数组尽可能的大,输出b数组
统计所有数字出现的次数和位置(这里使用了队列因为我觉得删的方便
对于每个mex将数字出现的位置维护
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int a[N];
queue<int> g[N];
vector<int> res;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
int n;
cin >> n;
res.clear();
for (int i = 1; i <= n; ++i) {
cin >> a[i];
g[a[i]].push(i);
}
int pos = 0;
while(pos < n) {
for (int i = 0; i <= n; ++i) {
if (g[i].empty()) {
res.push_back(i);
break;
}
pos = max(pos, g[i].front());
}
if(res[res.size() - 1] == 0) {
pos ++;
continue;
}
for(int i=0; i < res[res.size() - 1]; ++ i) {
while(g[i].size() && g[i].front() <= pos) g[i].pop();
}
}
cout << res.size() << endl;
for(auto tmp : res) cout << tmp <<" ";
cout << endl;
//init
for (int i = 0; i <= n; ++i) {
while(!g[i].empty()) g[i].pop();
}
}
return 0;
}
D. Peculiar Movie Preferences
题意:有n个字符串(长度不超过3),要求保留至少一个,使得他们连接起来形成回文串
如果一个串本身就是回文,直接留它就行,除去该情况之后
不然只要是两个拼起来是回文就行,即
(AB)(BA)
(ABC)(BA)
(AB)(CBA)
(ABC)(CBA)
对这些情况直接进行判断:对于BA和CBA可以构造出AB和ABC等,用set维护,剩下的一个ABC在前BA在后的额外倒过来扫一次
(至于为什么不用判别的不会证明但好像就是不用判,
证明可以看下官方题解
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
set<string> st;
string a[N];
inline bool check(string s) {
if (s.size() == 1) return 1;
if (s.size() == 2)
return (s[0] == s[1]);
if (s.size() == 3)
return (s[0] == s[2]);
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
st.clear();
bool flag = 0;
for (int i = 1; i <= n; ++i) {
string s;
cin >> s;
a[i] = s;
if (check(s)) flag = 1;
if (s.size() == 2) {
string t = "";
t = t + s[1] ;
t = t + s[0];
if (st.count(t)) flag = 1;
}
if (s.size() == 3) {
string t = "";
t = t + s[2];
t = t + s[1];
if (st.count(t)) flag = 1;
t = t + s[0];
if (st.count(t)) flag = 1;
}
st.insert(s);
}
if (flag) {
cout << "YES" << endl;
continue;
}
st.clear();
for (int i = n; i >= 1; --i) {
string s = a[i];
if (s.size() == 3) {
string t = "";
t = t+ s[1];
t = t + s[0];
if (st.count(t)) {
flag = 1;
break;
}
}
st.insert(s);
}
if (flag) {
cout << "YES" << endl;
} else {
cout << "NO" << endl;
}
}
}
E. Grid Xor
题意: n × n n \times n n×n 的格子里每个格子有一个数,现在告诉每个格子周围格子的异或和,问整个格子的总异或和是多少
直接转换成翻格子游戏(黑变白-白变黑那个)
相邻两个格子可以凑成六边形,然后六边形可以无缝拼接,只要再处理一下边角就行
然后第一排搞好后面的根据第一排铺就可以了
照常谢谢hoshimi同学,明天补代码,眠了
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1010;
ll a[N][N], b[N][N];
ll n;
inline void init() {
for (ll i = 0; i <= n; ++i) {
for (ll j = 0; j <= n; ++j) {
b[i][j] = 0;
a[i][j] = 0;
}
}
}
void sign(ll x, ll y) {
if (x - 1 >= 1 && x - 1 <= n) b[x - 1][y] = 1 - b[x - 1][y];
if (x + 1 >= 1 && x + 1 <= n) b[x + 1][y] = 1 - b[x + 1][y];
if (y - 1 >= 1 && y - 1 <= n) b[x][y - 1] = 1 - b[x][y - 1];
if (y + 1 >= 1 && y + 1 <= n) b[x][y + 1] = 1 - b[x][y + 1];
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
cin >> n;
for (ll i = 1; i <= n; ++i) {
for (ll j = 1; j <= n; ++j) {
cin >> a[i][j];
}
}
ll ans = 0;
for (ll i = 1; i <= n; i += 4) {
ans = ans ^ a[1][i];
ans = ans ^ a[1][i + 1];
sign(1, i), sign(1, i + 1);
}
for (ll i = 2; i <= n; ++i) {
for (ll j = 1; j <= n; ++j) {
if (b[i - 1][j] == 0) {
ans = ans ^ a[i][j];
sign(i, j);
}
}
}
cout << ans << endl;
init();
}
}
F1. Game on Sum (Easy Version)
题意:Alice和Bob在玩游戏,A先选一个在 [ 0 , k ] [0, k] [0,k]的实数,然后B决定从当前的结果中加上它还是减去它,一共有n轮,B在这n轮中至少要加m轮(B每次知道A当前的选择数是多少,A每次知道之前的结果),A要使得结果最大,B使得最小
考虑dp
d p [ i ] [ j ] : = dp[i][j]:= dp[i][j]:= 一共i轮 ,其中至少j轮是加的结果
然后有一些边界条件:
d p [ i ] [ 0 ] = 0 dp[i][0] = 0 dp[i][0]=0; //i轮全减的情况
d p [ i ] [ i ] = i × k dp[i][i] = i \times k dp[i][i]=i×k ; //i轮全加的情况
假设当前A选择的数是x,B要使得结果尽可能小, 则 D P [ i ] [ j ] = m i n ( D P [ i − 1 ] [ j − 1 ] + x , D P [ i − 1 ] [ j ] − x ) DP[i][j]=min(DP[i−1][j−1]+x,DP[i−1][j]−x) DP[i][j]=min(DP[i−1][j−1]+x,DP[i−1][j]−x)
注意到两边都是线性的,所以可以直接除以2, (这里可以用小数据来想一下,比如n = 2, m = 1的数据
于是得到最后的转移式子为
D P [ i ] [ j ] = ( D P [ i − 1 ] [ j − 1 ] + D P [ i − 1 ] [ j ] ) / 2 DP[i][j]=(DP[i−1][j−1]+DP[i−1][j])/2 DP[i][j]=(DP[i−1][j−1]+DP[i−1][j])/2
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2010;
const ll mod = 1e9 + 7;
ll dp[N][N];
ll n, m, k;
ll qpow(ll x, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
void solve() {
for (ll i = 1; i <= n; ++i) {
dp[i][0] = 0;
dp[i][i] = i * k % mod;
}
for (ll i = 2; i <= n; ++i) {
for (ll j = 1; j < i; ++j) {
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j]) % mod * qpow(2, mod - 2) % mod;
}
}
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll T;
cin >> T;
while (T--) {
cin >> n >> m >> k;
solve();
cout << dp[n][m] << endl;
}
return 0;
}
F2
在F1的基础上扩大了数据范围
需要优化dp的转移
观察转移可以发现dp初始值来源是
d
p
[
i
]
[
i
]
dp[i][i]
dp[i][i]
每次转移都除以2
则考虑这样一个转移图
每个
d
p
[
n
]
[
m
]
dp[n][m]
dp[n][m]的值的来源其实就是各个
d
p
[
i
]
[
i
]
dp[i][i]
dp[i][i] , 每个
d
p
[
i
]
[
i
]
dp[i][i]
dp[i][i] 到 n,m 走有两种移动,一种是直接往下,一种是往下1往右1, 然后每次除以2
就是从若干次往下的转移中挑一些往右
变成一个组合数了就
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e6 + 10;
const ll mod = 1e9 + 7;
ll n, m, k;
ll qpow(ll x, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll fac[N];// n!
ll invfac[N]; // n!的inv
ll invn[N]; //n的inv
inline void init() {
fac[0] = fac[1] = invfac[0] = invfac[1] = invn[0] = invn[1] = 1;
for (ll i = 2; i < N; ++i) {
fac[i] = fac[i - 1] * i % mod;
invn[i] = (mod - mod / i) * invn[mod % i] % mod;
invfac[i] = invfac[i - 1] * invn[i] % mod;
}
}
ll C(ll down, ll up) {
if (up > down) return 0;
if (up < 0 || down < 0) return 0;
ll res = fac[down];
res = res * invfac[down - up] % mod;
res = res * invfac[up] % mod;
return res;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
init();
ll T;
cin >> T;
while (T--) {
cin >> n >> m >> k;
if (m == n) {
cout << n * k % mod << endl;
continue;
}
if (m == 0) {
cout << 0 << endl;
continue;
}
ll ans = 0;
for (ll i = 1; i <= n; ++i) {
if (i > m) break;
ans = (ans + i * k % mod * C(n - i - 1, m - i) % mod
* qpow(qpow(2, n - i), mod - 2) % mod) % mod;
}
cout << ans << endl;
}
return 0;
}