LGP6798 「StOI-2」简单的树
原题链接:「StOI-2」简单的树
分析
首先,我需要一个 dfn,把这棵树拍到一个序列上,然后变成了一个区间 max \max max 问题?
对于一个点权值的修改,我们发现他似乎只会影响他的祖先,所以,我们难道要 reverse 一下 dfn 吗?那似乎......这玩意真的好做吗?
我们发现, l ′ , r ′ l',r' l′,r′ 这个区间巨大,不能每次都修改。对于那些不影响的部分,我们可以预处理一下,然后做。对于那些影响祖先的部分,我们发现这个东西依然有一个上届,且,上届似乎一直只会向根移动(为了方便,我觉得直接在 v a l val val 里讨论是正确的,诶,不完全对,如果一个点修改了......我们可以粗略证明,这个东西只会影响从其儿子开始再一次进行统计,不会影响更深的位置)。
那么,这种很区的优化可以获得随机的分数,因为我觉得出题人估计是随便卡的。
线段树精细实现可以完成吗?我把 dfn 搞到线段树上,然后......然后 G G G 了......倍增不支持修改啊😭
数学?这和数学有什么关系吗?
这个还有写的风险吗😭
T640835 【pty2022】编号
原题链接:【pty2022】编号
分析
就类似进制转换吧......🤮
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 15;
int n, k;
int a[N];
int qpow(int a, int b){
int res = 1;
while (b){
if (b & 1)
res *= a;
a *= a;
b >>= 1;
}
return res;
}
signed main(){
// freopen("number.in", "r", stdin);
// freopen("number.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> k;
for (int i = 1; i <= k; i++)
cin >> a[i];
if (a[1] == 0){
if (n == 1){
cout << 0 << '\n';
return 0;
}
n--;
vector<int> pos;
for (int i = 1; i <= k; i++){
if (a[i] != 0){
pos.push_back(a[i]);
}
}
int m = pos.size();
if (n <= m){
cout << pos[n - 1] << '\n';
return 0;
}
n -= m;
int len = 2;
while (true){
int cnt = m * qpow(k, len - 1);
if (n <= cnt)
break;
n -= cnt;
len++;
}
n--;
int tmp1 = qpow(k, len - 1);
int tmp2 = n / tmp1;
n %= tmp1;
cout << pos[tmp2];
for (int i = len - 2; i >= 0; i--){
int p = qpow(k, i);
int idx = n / p;
n %= p;
cout << a[idx + 1];
}
cout << '\n';
}
else{
int m = k;
if (n <= m){
cout << a[n] << '\n';
return 0;
}
n -= m;
int len = 2;
while (true){
int cnt = qpow(m, len);
if (n <= cnt)
break;
n -= cnt;
len++;
}
n--;
for (int i = len - 1; i >= 0; i--){
int p = qpow(m, i);
int idx = n / p;
n %= p;
cout << a[idx + 1];
}
}
}
CF2255A Hot Potatoes at the Fairy Warehouse
原题链接:A. Hot Potatoes at the Fairy Warehouse
分析
传说是一个博弈论。
这是容易的吗(・∀・(・∀・(・∀・(
秒了!我们发现最佳的博弈是保留 k − 1 k-1 k−1 轮给下一位。做完了!
嗯😭
咋这么聪明😭
正解
cpp
#include <bits/stdc++.h>
using namespace std;
int t;
int n, k;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
for (int cs = 1; cs <= t; cs++){
cin >> n >> k;
string s;
cin >> s;
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < (int)s.length(); i++){
int j = (i + 1) % (int)s.length();
if (s[i] == '1' && s[j] == '1'){
if (i % 2 == 0){
cnt1++;
}
else{
cnt2++;
}
}
if (s[i] == '1' && s[j] == '0'){
if (i % 2 == 0){
cnt2++;
}
else{
cnt1++;
}
}
}
cout << cnt2 << " " << cnt1 << '\n';
}
}
CF2255D How Long Until Nothing Remains?
原题链接:D. How Long Until Nothing Remains
分析
天啊,你会发现,这个东西最少操作 n n n 次,至多操作 n + log 2 n n+\log_2{n} n+log2n 次。
由于这个删的太困难了,考虑 狭义 hxf \operatorname{hxf} hxf 容斥,即 正难则反 。
那么,我们变成了从 0 0 0 开始加,每一次,如果选择这个数,则将其变成 a i × 2 + 1 a_{i}\times 2+1 ai×2+1,反之,变成 a i × 2 a_{i}\times 2 ai×2。由于要枚举 31 31 31 个数,常数有点大,所以,二分一下,那么,常数变成了 log 2 31 \log_{2}{31} log231,还是非常不错的。
至于我们的 check,开一个 priority_queue,然后每次处理堆顶,做完了。
就是说,如果有一个数大于当前 ,那就出问题了,反之,我们进行一下异或,因为这个都是一次一次 × 2 \times2 ×2 弄上来的,就做完了。
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 200005;
int t;
int n, a[N], pw[35];
bool cmp(int x, int val){
if (x >= 31)
return false;
if (pw[x] < val)
return true;
return false;
}
bool check(int x){
priority_queue<int> q;
for (int i = 1; i <= n; i++)
q.push(a[i]);
while (!q.empty()){
int tmp = q.top();
q.pop();
x--;
if (x < 0)
return false;
if (cmp(x + 1, tmp))
return false;
if (cmp(x, tmp)){
tmp ^= pw[x];
q.push(tmp);
}
}
return true;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
pw[0] = 1;
for (int i = 1; i < N; i++)
pw[i] = pw[i - 1] * 2;
for (int cs = 1; cs <= t; cs++){
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
int l = n, r = n + 31;
while (l < r){
int mid = (l + r) >> 1;
if (check(mid)){
r = mid;
}
else{
l = mid + 1;
}
}
cout << r << '\n';
}
}
LGP1290 欧几里德的游戏
原题链接:欧几里德的游戏
分析
不是,这俩哥们有点太聪明了吧???
看得出来,一旦出现倍数关系就意味着胜负已分。
这场博弈,分明就是老鼠洗完头,你一点赢他的几率都没有!!!
泥肘!!!
那么,两个人的最优决策就是祸水东引,让对方得出倍数关系,并且让 1 1 1 出现在对自己有利的位置上。
思考一下,其实是操作的总次数的奇偶性导致的胜负,所以,对于后续,我们发现似乎总会出现每次只能减一倍的情况,所以,第一次操作的时候,我们大概就要开始卡了。但是,这俩人都不傻,所以,会遇见一些东西吗?就是会漏一次......其实感觉博弈论的题,你开始想这种东西的时候已经偏了。要不然这就是个伪科学。
算了算了,看题解吧😭
对吧,其实我想到类似余数的东西了。
有点神啊......
就是说,相当于我们认为当前是 ( x , y ) (x,y) (x,y),且 y = k x + z y=kx+z y=kx+z,则这一步可以转到 ( x , y − ( k − 1 ) x ) (x,y-(k-1)x) (x,y−(k−1)x) 也就是 ( x , x + z ) (x,x+z) (x,x+z)。
然后,考虑 k ≥ 2 k\ge 2 k≥2 的情况,这个时候当前操作者必胜,就是相当于他有更多的冗余,可以操作这个游戏的导向......应该是这样的。
对于 k < 2 k<2 k<2,那就递归下去直到出现即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
int C;
int n, m;
int work(int a, int b, int winner){
if (a == b || a / b >= 2)
return winner;
return work(max(a - b, b), min(a - b, b), winner ^ 1);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> C;
for (int cs = 1; cs <= C; cs++){
cin >> m >> n;
int ans = work(max(n, m), min(n, m), 0);
if (ans == 0){
cout << "Stan wins" << '\n';
}
else{
cout << "Ollie wins" << '\n';
}
}
}