The Preliminary Contest for ICPC Asia Xuzhou 2019
Posted heyuhhh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了The Preliminary Contest for ICPC Asia Xuzhou 2019相关的知识,希望对你有一定的参考价值。
A. Who is better?
扩展中国剩余定理+斐波那契博弈,没啥好说的,关于斐波那契博弈,详见:传送门
Code
```cpp #includeB. so easy
并查集+\\(map\\)就行,还可以离散化搞一下,把\\(i+1\\)顺便离散化一下就行。
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e6+5,MAXM = 1e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N=100050;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define vii vector<pair<int,int>>
#define vi vector<int>
using namespace std;
int n,q,X[MAXN<<1],len,fa[MAXN<<1],v[MAXN],v2[MAXN],mp[MAXN<<1];
struct Ques
int z,x;
Q[MAXM];
int find(int x)
return x==fa[x]?x:fa[x]=find(fa[x]);
struct Istream
template <class T>
Istream &operator >>(T &x)
static char ch;static bool neg;
for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
x=neg?-x:x;
return *this;
fin;
int main()
//ios::sync_with_stdio(false);cin.tie(0);
//freopen("../A.in","r",stdin);
//freopen("../A.out","w",stdout);
fin>>n>>q;
for(register int i=1;i<=q;i++)
fin>>Q[i].z>>Q[i].x;
X[++len]=Q[i].x;
X[++len]=Q[i].x+1;
sort(X+1,X+1+len);
len=unique(X+1,X+1+len)-X-1;
for(int i=1;i<=len;i++)fa[i]=i;
for(int i=1;i<=q;i++)
v[i] = lower_bound(X+1,X+1+len,Q[i].x)-X;
v2[i] = lower_bound(X+1,X+1+len,Q[i].x+1)-X;
mp[v[i]] = Q[i].x;
mp[v2[i]] = Q[i].x+1;
int ans=0;
for(int i=1;i<=q;i++)
if(Q[i].z==1)
if(fa[v[i]]==v[i])fa[v[i]] = find(v2[i]);
else
ans=mp[find(v[i])];
if(ans==n+1)printf("-1\\n");
else printf("%d\\n",ans);
return 0;
C. Buy Watermelon
不知道啥题,队友说有点坑= =
Code
#include<bits/stdc++.h>
using namespace std;
int main()
int w; cin>>w;
if(w>=4 && w%2==0) puts("YES");
else puts("NO");
D. Carneginon
KMP模板题。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int q;
int nxtT[N], nxtS[N], nxt[N];
char T[N], S[N];
void Get_next(char *s, int *nxt)
int j, L = strlen(s + 1);
nxt[1] = j = 0;
for(int i = 2; i <= L; i++)
while(j && s[i] != s[j + 1]) j = nxt[j];
if(s[i] == s[j + 1]) j++;
nxt[i] = j;
bool cmp(char *s1, char *s2, int op)
if(op) memcpy(nxt, nxtS, sizeof(nxtS));
else memcpy(nxt, nxtT, sizeof(nxtT));
int L1 = strlen(s1 + 1), L2 = strlen(s2 + 1);
for(int i = 1, j = 0; i <= L1; i++)
while(j > 0 && (j == L2 || s1[i] != s2[j + 1])) j = nxt[j];
if(s1[i] == s2[j + 1]) j++;
if(j == L2) return true;
return false;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> T + 1 >> q;
Get_next(T, nxtT);
int lenT = strlen(T + 1);
while(q--)
cin >> S + 1;
int lenS = strlen(S + 1);
Get_next(S, nxtS);
if(lenS == lenT)
if(cmp(S, T, 1)) cout << "jntm!" << '\\n';
else cout << "friend!" << '\\n';
else if(lenS < lenT)
if(cmp(T, S, 1)) cout << "my child!" << '\\n';
else cout << "oh, child!" << '\\n';
else
if(cmp(S, T, 0)) cout << "my teacher!" << '\\n';
else cout << "senior!" << '\\n';
return 0;
E. XKC‘s basketball team
题意:
蔡徐坤的篮球队...
给出\\(n\\)个数,每个数为\\(w_i\\),现在对于第\\(i\\)个位置,找最远的一个\\(j\\),满足\\(w_j\\geq w_i+m\\)。
思路:
注意到这个\\(j\\)的选取具有单调性就行了,然后维护后缀最大值二分一下即可。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
int n, m;
int w[N], mx[N];
bool chk(int x, int i)
return mx[x] >= w[i] + m;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> w[i];
for(int i = n; i; i--) mx[i] = max(w[i], mx[i + 1]);
for(int i = 1; i <= n; i++)
int l = i + 1, r = n + 1, mid;
while(l < r)
mid = (l + r) >> 1;
if(chk(mid, i)) l = mid + 1;
else r = mid;
cout << l - 2 - i;
if(i != n) cout << ' ';
return 0;
F. Little M‘s attack plan
题意:
给出一颗\\(n\\)个结点的树,每个结点有权值\\(p_i\\)。
之后回答\\(q\\)个询问,每个询问给出\\(v_i,k_i\\),回答距离点\\(v_i\\)距离不大于\\(k_i\\)的所有点的权值和。
其中\\(q\\leq 5000,0\\leq k_i\\leq 100\\)。
思路:
- 思考这样一个问题,假设询问只包含子树结点的时候怎么做?
- 这样的做法有很多,可以主席树维护深度,在进入子树的时候统计一下答案,出子树的时候统计一下答案,两者相减即可。
- 但为啥要用主席树,这种事不是权值线段树或者权值树状数组就行了么...
- 现在回到原问题,注意\\(q,k\\)都比较小,两者相乘也才\\(5e5\\),所以想到将问题拆分!
- 假设现在的询问为\\((v,k)\\),当前在\\(v\\)子树中深度相差不超过\\(k\\)的询问的答案为\\(F(v,k)\\)。那么我们求出了\\(F(v,k)\\),还需要求\\(F(fa[v],k-1)\\),之后有一部分重合,我们就减去\\(F(v,k-2)\\)...然后依次这样算就行了。
总的来说,就是利用容斥+拆分询问的思想,有时候一个询问比较复杂,可以考虑将询问拆分成多个,每次只回答一些子问题就行了。这跟多维偏序有点像。。
注意一下细节,比如\\(k=0\\)的情况什么的,以及跳到根就没必要再跳了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000005;
template <class T>
inline void read(T& x)
static char c;
x = 0;
bool sign = 0;
while (!isdigit(c = getchar()))
if (c == '-')
sign = 1;
for (; isdigit(c); x = x * 10 + c - '0', c = getchar())
;
if (sign)
x = -x;
int n;
ll a[N];
struct Edge
int v, next;
e[N << 1];
int head[N], tot;
void adde(int u, int v)
e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
int fa[N], d[N];
void getfa(int u, int Fa)
fa[u] = Fa; d[u] = d[Fa] + 1;
for(int i = head[u]; i != -1; i = e[i].next)
int v = e[i].v;
if(v != Fa) getfa(v, u);
struct Query
int k, op, id;
;
vector <Query> q[N];
ll c[N];
int lowbit(int x) return x & (-x);
void add(int x, ll v)
for(; x < N; x += lowbit(x)) c[x] += v;
ll sum(int x)
ll ans = 0;
for(; x; x -= lowbit(x)) ans += c[x];
return ans;
ll sum(int l, int r)
return sum(r) - sum(l - 1);
ll res[N];
void dfs(int u, int fa)
for(auto it : q[u])
int k = it.k, id = it.id, op = it.op;
res[id] -= op * sum(d[u], d[u] + k);
add(d[u], a[u]);
for(int i = head[u]; i != -1; i = e[i].next)
int v = e[i].v;
if(v != fa) dfs(v, u);
for(auto it : q[u])
int k = it.k, id = it.id, op = it.op;
res[id] += op * sum(d[u], d[u] + k);
int main()
// freopen("input.in", "r", stdin);
read(n);
for(int i = 1; i <= n; i++) read(a[i]);
memset(head, -1, sizeof(head));
for(int i = 1; i < n; i++)
int u, v; read(u), read(v);
adde(u, v); adde(v, u);
getfa(1, 0);
int t; read(t);
for(int i = 1; i <= t; i++)
int v, k; read(v), read(k);
q[v].push_back(k, 1, i);
while(fa[v] && (--k) >= 0)
q[fa[v]].push_back(k, 1, i);
if(k - 1 >= 0) q[v].push_back(k - 1, -1, i);
v = fa[v];
// for(int i = 1; i <= tot; i++)
// cout << q[i].v << ' ' << q[i].k << ' ' << q[i].op << '\\n';
//
dfs(1, 0);
for(int i = 1; i <= t; i++)
printf("%lld\\n", res[i]);
return 0;
G. Colorful String
在回文自动机上面\\(dfs\\)一下,统计一下即可。利用一个桶来维护一下当前出现的次数,注意还要乘上回文串出现的次数。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
int n;
char s[N];
namespace PAM
int ch[N][26], fail[N], len[N], st[N], cnt[26], num[N];
int sz, n, last;
ll ans, cur;
int New(int l, int f)
memset(ch[++sz], 0, sizeof(ch[sz]));
len[sz] = l, fail[sz] = f;
return sz;
void init()
sz = -1; ans = cur = 0;
New(0, 1); last = New(-1, 0);
st[n = 0] = -1;
memset(cnt, 0, sizeof(cnt));
memset(num, 0, sizeof(num));
int getf(int x)
while(st[n - len[x] - 1] != st[n]) x = fail[x];
return x;
bool Insert(int c) //int
st[++n] = c;
int x = getf(last);
bool F = 0;
if(!ch[x][c])
F = 1;
int f = getf(fail[x]);
ch[x][c] = New(len[x] + 2, ch[f][c]);
last = ch[x][c];
++num[last];
return F;
void count()
for(int i = sz; i >= 2; i--) num[fail[i]] += num[i];
void debug()
cout << sz << '\\n';
for(int i = 1; i <= sz; i++) cout << num[i] << ' ';
cout << '\\n';
void dfs(int u)
for(int i = 0; i < 26; i++)
int v = ch[u][i];
if(v)
if(++cnt[i] == 1) ++cur;
ans += cur * num[v];
dfs(v);
if(--cnt[i] == 0) --cur;
;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> s + 1;
n = strlen(s + 1);
PAM::init();
for(int i = 1; i <= n; i++)
PAM::Insert(s[i] - 'a');
PAM::count();
PAM::dfs(0);
PAM::dfs(1);
cout << PAM::ans;
return 0;
H. function
参见:传送门
I. query
题意:
给出一个\\(n\\)的排列,现在回答多个询问,对于每个询问\\([l,r]\\),回答有多少对\\((i,j)\\),满足\\(l\\leq i < j\\leq r\\)且\\(min(p_i, p_j)=gcd(p_i,p_j)\\)。
思路:
注意到\\(min(p_i, p_j)=gcd(p_i,p_j)\\)其实就是\\(a_i,a_j\\)为倍数关系,因为\\(1\\)到\\(n\\)的排列中这样的倍数对数量级为\\(O(nlogn)\\)的,所以我们可以提前找出来所有的对数。
然后将询问离线,就相当于处理一个简单的二位偏序问题了。
有很多种做法,余独爱树状数组。
Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int n, m;
int a[N], p[N];
vector <int> v[N];
vector <pii> Q[N];
int ans[N];
int c[N];
int lowbit(int x) return x & (-x);
void add(int x, int v)
for(; x < N; x += lowbit(x)) c[x] += v;
int query(int x)
int ans = 0;
for(; x; x -= lowbit(x)) ans += c[x];
return ans;
int query(int l, int r)
return query(r) - query(l - 1);
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i]; p[a[i]] = i;
for(int i = 1; i <= n; i++)
for(int j = 2 * i; j <= n; j += i)
int x = p[i], y = p[j];
if(x > y) swap(x, y);
v[y].push_back(x);
for(int i = 1; i <= m; i++)
int l, r; cin >> l >> r;
Q[r].push_back(MP(l, i));
for(int i = 1; i <= n; i++)
for(auto it : v[i]) add(it, 1);
for(auto it : Q[i])
int L = it.fi, id = it.se, R = i;
ans[id] = query(L, R);
for(int i = 1; i <= m; i++) cout << ans[i] << '\\n';
return 0;
J. Random Access Iterator
题意:
现在有一个\\(vector\\),现在每次访问的时候会随机访问一个元素。
然后现在用这个\\(vector\\)去找子树最大深度,每到一个结点时,先找出\\(size\\),然后循环\\(size\\)次去找儿子\\(dfs\\)下去。
最后问得到的最大深度与实际真正的最大深度相等的概率为多少。
思路:
这个题我没想出来,dp这方面太弱了QAQ。
首先注意这一点:
- 你成功一次就说明你成功了,要想失败,就得全部失败。
这不是什么励志鸡汤,这一点可以告诉我们将问题转化一下,求出失败的概率,那么减一下就有成功的概率了。
考虑树形dp,设\\(dp[u]\\)表示从\\(u\\)出发,能成功到达最大深度的概率。初始化,对于一些深度为最大深度的叶子结点,它们的值为\\(1\\),其余全是\\(0\\)。
我们考虑对于每个结点,我们算它们失败一次的概率。那么就有:\\(p=1-\\frac1size\\sum_v\\in sondp[v]\\)。这个还是比较好算的。
那么既然不能成功,就说明每次失败,那么全部失败的概率就为\\(p^size\\)。所以\\(dp[u]=1-p^size\\)。
转化一下问题过后,就变得很简单了,如果硬要死磕成功,那么就还有第几次成功的问题,或者成功过后再成功什么乱七八糟的,就很难搞了。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5, MOD = 1e9 + 7;
ll qp(ll a, ll b)
ll ans = 1;
while(b)
if(b & 1) ans = ans * a % MOD;
a = a * a % MOD;
b >>= 1;
return ans;
int n;
vector <int> g[N];
int sz[N], dep[N];
int Max;
void dfs(int u, int fa)
dep[u] = dep[fa] + 1;
for(auto v : g[u])
if(v == fa) continue;
dfs(v, u);
++sz[u];
int dp[N];
int add(int x, int y)
x += y;
if(x >= MOD) x -= MOD;
return x;
int mul(ll a, ll b)
a *= b;
return a % MOD;
void dfs2(int u, int fa)
if(dep[u] == Max)
dp[u] = 1; return ;
int p = 0;
for(auto v : g[u])
if(v == fa) continue;
dfs2(v, u);
p = add(p, dp[v]);
p = mul(p, qp(sz[u], MOD - 2));
p = 1 - p + MOD;
int k = qp(p, sz[u]);
dp[u] = (1 - k + MOD) % MOD;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
for(int i = 1; i < n; i++)
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
dfs(1, 0);
Max = *max_element(dep + 1, dep + n + 1);
dfs2(1, 0);
cout << dp[1];
return 0;
K. Center
题意:
给出\\(n\\)个点,\\(n\\leq 1000\\),现在要在二维平面上找一个点\\((x_0,y_0)\\),使得对于每个点\\((x_i,y_i)\\),都存在一个点\\((x_j,y_j)\\)关于\\((x_0,y_0)\\)中心对称。
如果不存在一个点\\((x_j,y_j)\\),那么就称\\((x_i,y_i)\\)为孤儿点。
现在问怎么选择\\((x_0,y_0)\\),使得孤儿点最少。
思路:
- 注意到我们选择的这个点\\((x_0,y_0)\\)肯定位于某些个点的中线上,并且这些点的个数越多越好。
- 注意到点的个数很少,所以我们直接暴力枚举两个点,统计中点次数,然后更新答案就好了。
注意到如果这个点为某个\\((x_i,y_i)\\),那么还需要特判一下再更新答案。
详见代码:
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1005;
map <int, map<int, int>> mp, cnt;
struct node
int x, y;
a[N];
int n;
bool chk(int x, int y)
return x % 2 == 0 && y % 2 == 0 && mp[x / 2][y / 2] == 1;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i++)
cin >> a[i].x >> a[i].y;
mp[a[i].x][a[i].y] = 1;
int ansx, ansy;
int ans = 0;
for(int i = 1; i <= n; i++)
for(int j = i; j <= n; j++)
int x = a[i].x + a[j].x, y = a[i].y + a[j].y;
cnt[x][y]++;
if(cnt[x][y] > ans)
ans = cnt[x][y];
ansx = x, ansy = y;
else if(cnt[x][y] == ans && !chk(x, y))
ans = cnt[x][y];
ansx = x, ansy = y;
ans = n - 2 * cnt[ansx][ansy];
if(chk(ansx, ansy)) ++ans;
cout << ans;
return 0;
M. Longest subsequence
序列自动机预处理下一位,然后在每一位都有两种选择,等于或大于,跟着\\(t\\)串跑一遍就行了。
注意一下细节,注意字典序不能相等,只有严格大于。
详见代码吧:
Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
int nxt[N][26], last[26];
char s[N], t[N];
int n, m;
int main()
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> m;
cin >> s + 1 >> t + 1;
for(int i = n; i >= 0; i--)
int p = s[i] - 'a';
for(int j = 0; j < 26; j++) nxt[i][j] = last[j];
last[p] = i;
int i = 0;
int ans = -1;
for(int j = 1; j <= m; j++)
int tmp = N, p = t[j] - 'a';
for(int k = p + 1; k < 26; k++) if(nxt[i][k]) tmp = min(tmp, nxt[i][k]);
if(tmp < N) ans = max(ans, n - tmp + j);
i = nxt[i][p];
if(!i) break;
if(j == m && i < n)
ans = max(ans, m + n - i);
cout << ans;
return 0;
以上是关于The Preliminary Contest for ICPC Asia Xuzhou 2019的主要内容,如果未能解决你的问题,请参考以下文章
The Preliminary Contest for ICPC Asia Yinchuan 2019
The Preliminary Contest for ICPC Asia Shenyang 2019
The Preliminary Contest for ICPC Asia Shanghai 2019
The Preliminary Contest for ICPC Asia Shanghai 2019