CF gym-102452 2019-2020 ICPC Asia Hong Kong Regional Contest
B. Binary Tree
给你一棵树,两个人每次只能移走一颗子树,这个子树必须是满二叉树(叶子也行),第一个人先开始,谁不能操作谁就输了,问你谁能赢。
满二叉树是奇数个节点(\(2^x-1\)),那么每次都是移走奇数个点,判奇偶性就行了。
D. Defining Labels
进制转换,把给的数的10进制转化为带限制k进制,每一位都要在原本认知的基础上-1。
以k=10为例:
x = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 (十进制)
0 1 2 3 4 5 6 7 8 9 00 01 02 03 04 (十进制每一位还要-1)
如果要满足每一位-1,上面9和10是对应的,如果对每一位-1,那么10第一位-1就要把第二位退一位下来;如果先转10进制再模拟减法退位应该是可行的,代码量也应该不会很大。
还有一种做法是:
void solve(int k, int x)
{
if (!x)return;
x--;
solve(k, x / k);
ans.push_back((x % k) + 10 - k);
}
假设x=114514,k=5,我们虽然不知道x的5进制表示,但如果x的五进制表示的最后一位为0时,把x--之后它的五进制也自动进行了退位,不为0时就是最后一位-1,这样x%k得到的就是最后一位的位权,那么再把这个x/=k,相当于把x右移一位,这样递归下去直到x==0时就把x分解完了w
最后再加上题目条件10-k即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
#define ll long long
#define pii pair<int,int>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
ll mod = 1e9 + 7;
vector<int>ans;
void solve(int k, int x)
{
if (!x)return;
x--;
solve(k, x / k);
ans.push_back((x % k) + 10 - k);
}
int main()
{
fastio;
int t;
cin >> t;
while (t--)
{
ans.clear();
ll k, x;
cin >> k >> x;
solve(k, x);
for (auto i : ans)
cout << i;
cout << endl;
}
return 0;
}
E. Erasing Numbers
给长度为n的排列,每次操作选择连续的3个数,然后删除最大的和最小的数,进行很多次最终留下一个数。问你这个排列中哪些数可以在进行某一系列操作之后留下来。
记当前的数为x,比它大的数变为1,比它小的数变为0,比较0和1的个数:
①如果0和1的个数相同,那么肯定可以让这个数最终留下来(给出一种示例操作:由于0和1的个数相同,那么每次操作最理想的就是移除一个0一个1。出现0x1和1x0时移除x两侧的0和1;剩下的情况全部枚举出来:000、111、011、100、。。。操作一个000时肯定要对应的操作一个111,不过大部分时候连续0周围都会有1,这样就可以满足移除一个0一个1,连续1同理)
②如果0和1的个数不同,那么就要把多出来的数删除。由于只存在两种删除模式(删除2个相同的数(3个连续相同的数可以这样)和各删除一个),很显然只能去寻找删除2个相同的数这种情况。
让cnt0和cnt1做差,如果为奇数就凑不出来;
否则就再里面找连续的去删,看看能不能删到0和1的数量相同的状态,再去用各减1的操作处理。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 1e6 + 10;
ll mod = 998244353;
bool ans[maxn];
int n;
int solve(int x,vector<int>&a)
{
int cnt = 0, tot = 0;
for (int i = 1; i <= n; i++)
if (a[i] > a[x])cnt++;
else if (a[i] < a[x])tot++;
if (cnt == tot)return 1;
if (abs(cnt - tot) & 1)return 0;
int flag = (cnt > tot ? 1 : -1);
int sum = 0;
for (int i = 1; i <= n; i++)
{
if (i == x)
{
sum = 0;
continue;
}
if (a[i] > a[x])
sum += flag;
else sum -= flag;
if (sum < 0)
sum = 0;
if (sum == 3)
{
if (flag == 1)
cnt -= 2;
else tot -= 2;
sum = 1;
if (cnt == tot)
return 1;
}
}
return 0;
}
int main()
{
fastio;
int t;
cin >> t;
while (t--)
{
cin >> n;
vector<int>a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i], ans[i] = 0;
for (int i = 1; i <= n; i++)
ans[i] = solve(i, a);
for (int i = 1; i <= n; i++)
cout << ans[i] << "";
cout << endl;
}
return 0;
}
G. Game Design
构造一棵树,每个点可以安排一个权值,选择它获得权值并覆盖它可以到达的所有叶子节点。需要构造的这棵树有k种权值和最小的选择方式选择一些点覆盖所有的叶子节点。
在纸上画画可以发现一个规律,对于两棵树,把他们连向一个构造根,操作数相乘,当这个根的权值等于两棵树所有叶子节点的权值和时操作数再+1,否则不加。(说简单一点就是子树合并满足乘法原理,连向的根需要权值特判)
可以想到一个数可以被这种方式分解:\(117 = 116 + 1 = (58 * 2) + 1 =(29 * 2 * 2) + 1 = ((28 + 1) * 2 * 2) + 1\) .......
每次+1可以构造新根连向上一个构造根,*2可以把一个大小为2的链连向当前构造根。
最后把叶子节点的所有权值赋为1,跑一遍dfs统计一下各个非叶子节点的权值。
如果k一开始就是偶数那么这棵树的root可以赋个无限大权值就不用-1了。
由于这个数只有1e9,大概质因数分解也可以构造,k更大一些出现大质数就不能质因数分解了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 10;
#define ll long long
#define pii pair<int,int>
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
ll mod = 1e9 + 7;
int c[maxn], fa[maxn];
vector<int>G[maxn];
int dfs(int from)
{
int res = G[from].empty();
for (auto to : G[from])
res+=dfs(to);
return c[from] = res;
}
int main()
{
fastio;
int k;
cin >> k;
int K = k;
if (k == 1)
{
cout << 2 << endl;
cout << 1 << endl;
cout << 10000 << " " << 1 << endl;
return 0;
}
c[1] = (k & 1 ? 1 : 1e8);
if (k & 1)
k--;
int cnt = 1;
int last = 1;
int tot = 1;
while (k > 1)
{
while (k % 2 == 0)
{
++cnt;
G[last].push_back(cnt);
fa[cnt] = last;
++cnt;
G[cnt - 1].push_back(cnt);
fa[cnt] = cnt - 1;
k /= 2;
}
if (k == 1)
break;
k--;
++cnt;
G[last].push_back(cnt);
fa[cnt] = last;
last = cnt;
}
dfs(1);
if (K % 2 == 0)
c[1] = 1e8;
cout << cnt << endl;
for (int i = 2; i <= cnt; i++)
cout << fa[i] << " ";
cout << endl;
for (int i = 1; i <= cnt; i++)
cout << c[i] << " ";
return 0;
}
J. Junior Mathematician
让你求L到R区间内满足条件的在模m意义下的x同余f(x)的x的个数。
f(x)的定义:
这个式子含义就是:累加每一位的贡献,它的贡献就是它的权值乘上len到上一位的前缀和。
以下为设计状态的过程,WA+TLE了一上午
很显然是数位dp,最开始想dp[pos][limit][][],后两维表示pos到len所有位的和、所有位累加的f(x),然后发现记录前缀和相同的状态它们所对应的前面一段x对m取模可能不同,故又加上一维,然后爆空间了。
然后发现\(f(x)\equiv x(mod m)\)可以变成\(f(x)-x \equiv 0(mod m)\),这样后面又只需要两维:对于当前位置pos,第一维是pos到len这一段x对m取模的权值,第二维是pos到len这一段f(x)-x的权值(要+m %m弄成正的)。
第一维我尝试去用
(X * 10 + i) % m
去取前缀模,发现是错的,因为这种取模方式得到的结果要跑完整个长度才是正确的(后面很多个*10都没有累乘到前面去取模)
需要先去打一个表,把每一位需要乘多少个10记录下来:
for (int i = 1; i < max(s.length(), p.length()); i++)
biao[i] = biao[i - 1] * 10 % m;
导致TLE35的原因之一,计算多了,形参多了:
最后就是ac代码,这个题目卡常,不该取模的地方不要取模,函数的形参能少开就少开:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
const int inf = 1e9 + 7;
const ll lnf = 1e18 + 7;
const int maxn = 1e6 + 10;
int mod = 1e9 + 7;
int m;
int dp[5005][65][65];
int biao[5050];
int len = 0, b[5050];
int dfs(int pos, int flag, int res, int sum)
{
if (pos == -1)
return res == 0;
if (!flag && ~dp[pos][sum][res])return dp[pos][sum][res];
int r = flag ? b[pos] : 9;
ll tmp = 0;
for (int i = 0; i <= r; i++)
tmp += dfs(pos - 1, flag && i == b[pos], ((res + sum * i - i * biao[pos]) % m + m) % m, (sum + i) % m);
tmp %= mod;
if (!flag)
return dp[pos][sum][res] = tmp;
else
return tmp;
}
void init(int len)
{
//memset(dp, -1, sizeof(dp));
for (int i = 0; i < len; i++)
for (int k = 0; k < m; k++)
for (int l = 0; l < m; l++)
dp[i][k][l] = -1;
}
int main()
{
fastio;
int t;
cin >> t;
biao[0] = 1;
while (t--)
{
string s, p;
cin >> s >> p >> m;
for (int i = 1; i < max(s.length(), p.length()); i++)
biao[i] = biao[i - 1] * 10 % m;
int ok = 0;
for (int i = s.length() - 1; i >= 0; i--)
{
if (s[i] == '0')
s[i] = '9';
else
{
s[i] --;
break;
}
}
if (s[0] == '0')ok = 1;
len = 0;
init(max(s.length(), p.length()));
for (int i = s.length() - 1; i >= ok; i--)
b[len++] = s[i] - '0';
int res1 = dfs(len - 1, 1, 0, 0);
len = 0;
for (int i = p.length() - 1; i >= 0; i--)
b[len++] = p[i] - '0';
int res2 = dfs(len - 1, 1, 0, 0);
cout << (res2 - res1 + mod) % mod << "\n";
}
return 0;
}