The Preliminary Contest for ICPC Asia Xuzhou 2019
A What is better?
推不出来,写个程序打表,用扩展中国剩余定理合并,居然会溢出longlong,还好不会溢出__int128(赛后exit(-1)测试),实际证明溢出返回-1是不靠谱的,毕竟后面可以又把它搞小了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128 lll;
const int MAXK = 10 + 5;
void exgcd(lll a, lll b, lll &x, lll &y) {
if(!b)
x = 1, y = 0;
else
exgcd(b, a % b, y, x), y -= a / b * x;
}
lll inv(ll a, ll b) {
lll x = 0, y = 0;
exgcd(a, b, x, y);
x = (x % b + b) % b;
if(!x)
x += b;
return x;
}
int k;
lll c[MAXK], m[MAXK];
lll exCRT(int k) {
lll c1, c2, m1, m2, t;
for(int i = 2; i <= k; ++i) {
m1 = m[i - 1], m2 = m[i], c1 = c[i - 1], c2 = c[i];
t = __gcd(m1, m2);
if((c2 - c1) % t != 0)
return -1;
m[i] = m1 / t * m2;
if(m[i] <= 0)
exit(-1);
c[i] = inv(m1 / t, m2 / t) * ((c2 - c1) / t) % (m2 / t) * m1 + c1;
c[i] = (c[i] % m[i] + m[i]) % m[i];
}
return c[k];
}
/*map<pair<int, int>, pair<bool, int> > M;
bool dfs(int id, int pre) {
if(M.count({id, pre}))
return M[ {id, pre}].first;
if(pre == -1) {
for(int i = 1; i < id; ++i) {
if(dfs(id - i, i) == false) {
M[{id, pre}] = {true, i};
return true;
}
}
M[{id, pre}] = {false, -1};
return false;
} else {
int c = min(2 * pre, id);
if(c == id) {
M[{id, pre}] = {true, c};
return true;
}
for(int i = 1; i <= c; ++i) {
if(dfs(id - i, i) == false) {
M[{id, pre}] = {true, i};);
return true;
}
}
M[{id, pre}] = {false, -1};
return false;
}
}*/
ll f[72 + 5];
int main() {
#ifdef local
freopen("lyz.in", "r", stdin);
#endif // local
/*for(int i = 2; i <= 100; ++i) {
printf("i=%d", i);
if(dfs(i, -1)) {
printf(" WIN\n");
printf(" TO TAKE %d\n", M[ {i, -1}].second);
} else
printf(" FAIL\n");
}
for(int j = 1; j <= 20; ++j) {
for(int k = -1; k <= 20; ++k) {
if(M.count({j, k})) {
printf("(%d,%d) \n", j, k);
if(M[ {j, k}].first == true) {
printf(" WIN\n");
printf(" TO TAKE %d\n", M[ {j, k}].second);
} else {
printf(" FAIL\n");
}
puts("");
}
}
}*/
int k;
scanf("%d", &k);
bool suc = 1;
for(int i = 1; i <= k; ++i) {
ll tmp1, tmp2;
scanf("%lld%lld", &tmp1, &tmp2);
m[i] = tmp1;
c[i] = tmp2;
if(c[i] > 1e15) {
suc = false;
}
}
if(!suc) {
puts("Tankernb!");
return 0;
}
lll n = exCRT(k);
if(n <= 1 || n > 1e15) {
puts("Tankernb!");
return 0;
}
f[1] = 2;
f[2] = 3;
for(int i = 3; i <= 72; ++i) {
f[i] = f[i - 1] + f[i - 2];
if(f[i] >= 1e15) {
//cout<<"i="<<i<<endl;
break;
}
}
for(int i = 1; i <= 72; ++i) {
if(n == f[i]) {
puts("Lbnb!");
return 0;
}
}
puts("Zgxnb!");
return 0;
}
B so easy
一开始1e6弄个set莽了两次,果断T了,事实证明平衡树的常数的确相比离散化是在是太大了。用个并查集维护,删除一个节点的时候,假如他没有被删除过,那么就把他指向他的下一个元素(无论他的下一个元素是不是被删了都可以),并且把这个元素设置为“删除”,下面参照非递归路径压缩并查集弄了一个非递归路径压缩,实测727ms还算可以。
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
ch = getchar();
do {
x = (x << 3) + (x << 1) + ch - '0';
ch = getchar();
} while(ch >= '0' && ch <= '9');
return x;
}
const int MAXN = 1e6;
int z[MAXN + 5], x[MAXN + 5];
int px[2 * MAXN + 5], xtop;
int nxt[2 * MAXN + 5];
bool vis[2 * MAXN + 5] = {};
inline int find(int i) {
int cur = nxt[i];
while(vis[cur]) {
cur = nxt[cur];
}
while(nxt[i] != cur) {
int t = nxt[i];
nxt[i] = cur;
i = t;
}
return cur;
}
int main() {
#ifdef local
freopen("lyz.in", "r", stdin);
#endif // local
int n = read(), q = read();
xtop = 0;
for(int i = 1; i <= q; ++i) {
z[i] = read(), x[i] = read();
px[++xtop] = x[i];
if(z[i] == 1)
px[++xtop] = x[i] + 1;
}
sort(px + 1, px + 1 + xtop);
xtop = unique(px + 1, px + 1 + xtop) - (px + 1);
for(int i = 1; i <= q; ++i)
x[i] = lower_bound(px + 1, px + 1 + xtop, x[i]) - (px);
for(int i = 1; i <= xtop; ++i)
nxt[i] = i;
for(int i = 1; i <= q; ++i) {
if(z[i] == 1) {
if(!vis[x[i]]) {
vis[x[i]] = true;
nxt[x[i]] = nxt[x[i] + 1];
find(x[i]);
}
} else {
int res = find(x[i]);
printf("%d\n", px[res]);
}
}
}
J Random Access Iterator
一个逗逼签到题,根据期望的线性性蛮好推的,首先第一遍dfs算出最大深度和各个节点的深度(虽然各个节点的深度是不必要的),第二遍dfs把dp数组算出来。设dp[i]表示i的子树给出最大深度的概率。那么对于深度为最大深度的叶子节点,dp[i]=1,其他叶子dp[i]=0。那么对于中间节点,它的所有k个子节点能提供最大深度的概率的期望就是各个子节点的dp值的平均值。它不能给出正确深度的概率就是这个期望的补数连续乘k次。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e6;
vector<int> G[MAXN + 5];
int qpow(ll x, int n) {
ll res = 1;
while(n) {
if(n & 1)
res = res * x % MOD;
x = x * x % MOD;
n >>= 1;
}
return res;
}
int dp[MAXN + 5];
int MAXD;
void dfs1(int u, int p, int d) {
MAXD = max(MAXD, d);
for(auto v : G[u]) {
if(v != p) {
dfs1(v, u, d + 1);
}
}
return;
}
void dfs2(int u, int p, int d) {
if(d == MAXD) {
dp[u] = 1;
return;
}
ll P = 0;
int n = 0;
for(auto v : G[u]) {
if(v != p) {
dfs2(v, u, d + 1);
P += dp[v];
++n;
}
}
P = P * qpow(n, MOD - 2) % MOD;
ll Q = (1ll - P + MOD) % MOD;
Q = qpow(Q, n);
dp[u] = (1ll - Q + MOD) % MOD;
return;
}
int main() {
#ifdef local
freopen("lyz.in", "r", stdin);
#endif // local
int n;
scanf("%d", &n);
for(int i = 1; i <= n - 1; ++i) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs1(1, -1, 1);
dfs2(1, -1, 1);
printf("%d\n", dp[1]);
}
M Longest subsequence
设dp[i]表示匹配t串的长度为i的前缀需要用到s的最短长度,特别地为了统一,dp[0]=0,INF表示无法匹配。设pos[i][ch]表示在s串的i位置及其以后第一个ch出现的下标。
那么只有两种情况,第一,匹配了长度为i的前缀,然后从第i+1个字符开始严格大,i从0开始,这样就暴力一遍比t[i+1]大的最近的pos就行了。第二,完全匹配t,然后后面有多少加多少,注意至少要加一个!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF = 1e9;
const int MAXN = 1e6;
char s[MAXN + 5], t[MAXN + 5];
int dp[MAXN + 5];
int pos[MAXN + 5][26];
int main() {
#ifdef local
freopen("lyz.in", "r", stdin);
#endif // local
int n, m;
while(~scanf("%d%d", &n, &m)) {
scanf("%s%s", s + 1, t + 1);
dp[0] = 0;
for(int i = 1; i <= m; ++i)
dp[i] = INF;
for(int i = 1; i <= m; ++i) {
for(int j = dp[i - 1] + 1; j <= n; ++j) {
if(s[j] == t[i]) {
dp[i] = j;
break;
}
}
if(dp[i] == INF)
break;
}
for(int i = 0; i < 26; ++i)
pos[n + 1][i] = INF;
for(int i = n; i >= 1; --i) {
for(int j = 0; j < 26; ++j)
pos[i][j] = pos[i + 1][j];
pos[i][s[i] - 'a'] = i;
}
int ans = -1;
for(int i = 0; i <= m; ++i) {
int last = INF;
if(dp[i] == INF)
break;
else {
if(i == m) {
if(dp[i] != n)
ans = max(ans, i + n - (dp[i] + 1) + 1);
} else {
for(int j = t[i + 1] - 'a' + 1; j < 26; ++j)
last = min(last, pos[dp[i] + 1][j]);
ans = max(ans, i + n - last + 1);
}
}
}
printf("%d\n", ans);
}
}