Contest Info


[Practice Link](https://www.jisuanke.com/contest/3098?view=challenges)

Solved A B C D E F G H I J K
9/11 O O - - Ø O Ø O Ø O O
  • O 在比赛中通过
  • Ø 赛后通过
  • ! 尝试了但是失败了
  • - 没有尝试

Solutions


A. Attack

题意:

有\(n\)个城市,\(m\)条路可以建造,每条路有边权,现在要使得四对城市之间连通(每对城市连通就行,不用四对相互连通),问建造路的最小代价,如果有共同的路,那么不会重复建造。

思路:

  • 如果是四对城市互相连通,那么就是裸的斯坦纳树
  • 那么考虑这个问题中不好处理的就是有重复边使得答案更优的情况,怎么避免重复计算贡献?
  • 考虑如果在最优答案中有重复边,那么这条重复边所关联的几对城市放在一起做斯坦纳树,不会增加新的边
  • 那么就暴力组合一下,更新答案即可。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define N 1010
#define pii pair <int, int>
#define fi first
#define se second
int n, m;
int P[4][2];
vector <vector<pii>> G;
map <string, int> mp; int cnt;
int get(string s) {
if (mp.find(s) == mp.end()) {
return mp[s] = ++cnt;
}
return mp[s];
} struct SteinerTree {
int st[32], dp[32][1 << 8], endSt;
bool vis[32][1 << 8];
queue <int> que;
void init(int n, vector <int> &vec) {
sort(vec.begin(), vec.end());
vec.erase(unique(vec.begin(), vec.end()), vec.end());
memset(dp, -1, sizeof dp);
memset(vis, 0, sizeof vis);
memset(st, 0, sizeof st);
endSt = 1;
for (auto it : vec) {
st[it] = endSt;
endSt <<= 1;
}
for (int i = 1; i <= n; ++i) {
dp[i][st[i]] = 0;
}
}
void update(int &a, int x) {
a = (a > x || a == -1) ? x : a;
}
void SPFA(int state) {
while (!que.empty()) {
int u = que.front(); que.pop();
vis[u][state] = false;
for (auto it : G[u]) {
int v = it.fi, w = it.se, y = st[v] | state;
if (dp[v][y] == -1 || dp[v][y] > dp[u][state] + w) {
dp[v][y] = dp[u][state] + w;
if (y != state || vis[v][state])
continue;
vis[v][state] = true;
que.push(v);
}
}
}
}
int solve() {
for (int j = 1; j < endSt; ++j) {
for (int i = 1; i <= n; ++i) {
if (st[i] && (st[i] & j) == 0) continue;
for (int sub = (j - 1) & j; sub; sub = (sub - 1) & j) {
int x = st[i] | sub, y = st[i] | (j - sub);
if (dp[i][x] != -1 && dp[i][y] != -1) {
update(dp[i][j], dp[i][x] + dp[i][y]);
}
}
if (dp[i][j] != -1) {
que.push(i), vis[i][j] = true;
}
}
SPFA(j);
}
int res = 1e9;
for (int i = 1; i <= n; ++i) if (dp[i][endSt - 1] != -1) {
res = min(res, dp[i][endSt - 1]);
}
return res;
}
}ST; int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
while (cin >> n >> m) {
cnt = 0; mp.clear();
G.clear(); G.resize(n + 1);
for (int i = 1; i <= n; ++i) {
string s; cin >> s;
get(s);
}
for (int i = 1, u, v, w; i <= m; ++i) {
string s;
cin >> s; u = get(s);
cin >> s; v = get(s);
cin >> w;
G[u].push_back(pii(v, w));
G[v].push_back(pii(u, w));
}
vector <int> vec;
for (int i = 0; i < 4; ++i) {
string s;
cin >> s; P[i][0] = get(s);
cin >> s; P[i][1] = get(s);
}
int res = 1e9, tmp;
//1 + 1 + 1 + 1
tmp = 0;
for (int i = 0; i < 4; ++i) {
vec.clear();
vec.push_back(P[i][0]);
vec.push_back(P[i][1]);
ST.init(n, vec); tmp += ST.solve();
}
res = min(res, tmp); // 3 + 1
for (int i = 0; i < 4; ++i) {
tmp = 0;
vec.clear();
vec.push_back(P[i][0]);
vec.push_back(P[i][1]);
ST.init(n, vec); tmp += ST.solve();
vec.clear();
for (int j = 0; j < 4; ++j) {
if (i == j) continue;
vec.push_back(P[j][0]);
vec.push_back(P[j][1]);
}
ST.init(n, vec); tmp += ST.solve();
res = min(res, tmp);
} // 2 + 2
for (int i = 0; i < 4; ++i) {
for (int j = i + 1; j < 4; ++j) {
tmp = 0;
vec.clear();
vec.push_back(P[i][0]);
vec.push_back(P[i][1]);
vec.push_back(P[j][0]);
vec.push_back(P[j][1]);
ST.init(n, vec); tmp += ST.solve();
vec.clear();
for (int k = 0; k < 4; ++k) if (k != i && k != j) {
vec.push_back(P[k][0]);
vec.push_back(P[k][1]);
}
ST.init(n, vec); tmp += ST.solve();
res = min(res, tmp);
}
} // 2 + 1 + 1
for (int i = 0; i < 4; ++i) {
for (int j = i + 1; j < 4; ++j) {
tmp = 0;
vec.clear();
vec.push_back(P[i][0]);
vec.push_back(P[i][1]);
vec.push_back(P[j][0]);
vec.push_back(P[j][1]);
ST.init(n, vec); tmp += ST.solve();
for (int k = 0; k < 4; ++k) if (k != i && k != j) {
vec.clear();
vec.push_back(P[k][0]);
vec.push_back(P[k][1]);
ST.init(n, vec); tmp += ST.solve();
}
res = min(res, tmp);
}
} // 4
vec.clear();
for (int i = 0; i < 4; ++i) {
vec.push_back(P[i][0]);
vec.push_back(P[i][1]);
}
ST.init(n, vec);
res = min(res, ST.solve());
cout << res << "\n";
}
return 0;
}

B. Polynomial

题意:

给出一个多项式的第\(0\)项到第\(n\)项,询问:

\[\begin{eqnarray*}
\sum\limits_{i = l}^r f(i) \bmod 9999991
\end{eqnarray*}
\]

思路:

  • 考虑一个\(n\)次多项式的前缀和是一个\(n + 1\)次的多项式。
  • 插值出第\(n + 1\)项,就可以再插值出前缀和了

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 1100
const ll p = 9999991;
int n, m;
ll f[N], fac[N], inv[N];
ll Inv[10000010]; ll qmod(ll base, ll n) {
ll res = 1;
while (n) {
if (n & 1) {
res = res * base % p;
}
base = base * base % p;
n >>= 1;
}
return res;
} ll solve(ll *f, int n, int x) {
if (x <= n) return f[x];
int t = (n & 1) ? -1 : 1;
ll res = 0;
ll base = 1;
for (int i = 0; i <= n; ++i) base = base * (x - i) % p;
for (int i = 0; i <= n; ++i, t *= -1) {
res += 1ll * t * f[i] * base % p * inv[n - i] % p * inv[i] % p * Inv[x - i] % p;
res = (res + p) % p;
}
return res;
} int main() {
fac[0] = 1;
for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % p;
inv[N - 1] = qmod(fac[N - 1], p - 2);
for (int i = N - 1; i >= 1; --i) inv[i - 1] = inv[i] * i % p;
Inv[1] = 1;
for (int i = 2; i < p; ++i) Inv[i] = Inv[p % i] * (p - p / i) % p;
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; ++i) scanf("%lld", f + i);
f[n + 1] = solve(f, n, n + 1);
for (int i = 1; i <= n + 1; ++i) f[i] = (f[i] + f[i - 1]) % p;
int l, r;
while (m--) {
scanf("%d%d", &l, &r);
printf("%lld\n", (solve(f, n + 1, r) - solve(f, n + 1, l - 1) + p) % p);
}
}
return 0;
}

E. Interesting Trip

题意:

询问有多少条长度为\(D\)的路径中,满足这条路径上所有点的点权的\(gcd > 1\)。

思路:

我们先统计出所有长度为\(D\)的路径,然后减去点权的\(gcd = 1\)的路径即为答案。

怎么统计点权\(gcd = 1\)的路径?

我们可以考虑莫比乌斯反演,统计\(gcd \;|\; d\)的路径,即将所有点权为\(d\)的倍数的点构成的森林中长度为\(D\)的路径条数。

然后就是经典的容斥,容斥系数是莫比乌斯函数。

考虑到点权在\(10^4\)以下的数的不含有平方因子的因数最多有\(32\)个,也就是说每个点最多会被放到\(32\)个森林中,那么所有森林的总点数大概是\(O(32n)\)的。

那么考虑求定长路径条数可以用基于深度的\(dp\)解决,那么可以做到\(O(n)\)的合并。

所以总复杂度为\(O(32n)\)

代码:

view code
#include <bits/stdc++.h>
using namespace std;
namespace IO {
const int S=(1<<20)+5;
//Input Correlation
char buf[S],*H,*T;
inline char Get() {
if(H==T) T=(H=buf)+fread(buf,1,S,stdin);
if(H==T) return -1;return *H++;
}
inline int read() {
int x=0,fg=1;char c=Get();
while(!isdigit(c)&&c!='-') c=Get();
if(c=='-') fg=-1,c=Get();
while(isdigit(c)) x=x*10+c-'0',c=Get();
return x*fg;
}
}using namespace IO;
typedef long long ll;
const int N = 5e5 + 10;
const int M = 3e4 + 10;
int n, D, a[N], pri[M], check[M], mu[M], fa[N], deep[N], vis[N], md[N], hson[N]; ll res, F[N];
vector<vector<int>> dvec, fac;
struct Edge {int v, nx;}e[N << 1]; int h[N];
inline void addedge(int u, int v) { e[++*h] = {v, h[u]}; h[u] = *h; } void sieve() {
fac.clear(); fac.resize(M);
for (int i = 1; i < M; ++i)
for (int j = i; j < M; j += i)
fac[j].push_back(i);
*pri = 0;
mu[1] = 1;
for (int i = 2; i < M; ++i) {
if (!check[i]) {
pri[++*pri] = i;
mu[i] = -1;
}
for (int j = 1; j <= *pri; ++j) {
if (i * pri[j] >= M) break;
check[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
else mu[i * pri[j]] = -mu[i];
}
}
} void pre(int u) {
for (int i = h[u]; i; i = e[i].nx) {
int v = e[i].v;
if (v == fa[u]) continue;
deep[v] = deep[u] + 1;
fa[v] = u;
pre(v);
}
for (auto &it : fac[a[u]])
dvec[it].push_back(u);
} int tmp[N << 2], *f[N], *id = tmp;
void getdeep(int u) {
md[u] = deep[u];
hson[u] = 0;
for (int i = h[u]; i; i = e[i].nx) {
int v = e[i].v;
deep[v] = deep[u] + 1;
getdeep(v);
if (!hson[u] || md[v] > md[hson[u]]) hson[u] = v;
}
md[u] = md[hson[u]] + 1;
} void dfs(int u) {
if (hson[u]) {
int v = hson[u];
f[v] = f[u] + 1;
dfs(v);
}
f[u][0] = 1;
if (md[u] > D) {
res += f[u][D];
}
for (int i = h[u]; i; i = e[i].nx) {
int v = e[i].v;
if (v == hson[u]) continue;
f[v] = id; id += md[v] + 1;
dfs(v);
for (int i = 0; i < md[v]; ++i) {
if (md[u] > D - i - 1 && D - i - 1 >= 0) {
res += 1ll * f[u][D - i - 1] * f[v][i];
}
}
for (int i = 0; i < md[v]; ++i)
f[u][i + 1] += f[v][i];
}
} inline void gao(vector <int> &vec, int x) {
res = 0; h[0] = 0;
for (auto &u : vec) h[u] = 0, vis[u] = x;
for (auto &u : vec) {
if (vis[fa[u]] == x) {
addedge(fa[u], u);
} else {
deep[u] = 0;
getdeep(u);
id = tmp;
f[u] = id;
id += md[u] + 1;
dfs(u);
}
}
} int main() {
sieve();
int _T; _T = read();
for (int kase = 1; kase <= _T; ++kase) {
printf("Case #%d: ", kase);
n = read(); D = read();
dvec.clear(); dvec.resize(M);
memset(vis, 0, sizeof vis);
memset(h, 0, sizeof h);
for (int i = 1; i <= n; ++i) a[i] = read();
for (int i = 1, u, v; i < n; ++i) {
u = read(); v = read();
addedge(u, v);
addedge(v, u);
}
fa[1] = 0; deep[1] = 0;
pre(1);
for (int i = 1; i <= 30000; ++i) if (mu[i]) {
gao(dvec[i], i);
F[i] = res;
}
res = F[1];
for (int i = 1; i <= 30000; ++i) if (mu[i])
res -= 1ll * mu[i] * F[i];
printf("%lld\n", res * 2);
}
return 0;
}

F. Sequence

题意:

定义:

\[\begin{eqnarray*}
f(l, r) &=& \oplus_{i = l}^r a_i \\
F(l, r) &=& \oplus_{i = l}^r \oplus_{j = i}^r f(i, j)
\end{eqnarray*}
\]

给出一个序列\(a_i\),要求支持两种操作:

  • 将\(a_x\)修改成\(y\)
  • 询问\(F(l, r)\)

思路:

我们发现直接去计算这个式子比较不好计算,但是注意到异或的一个性质就是一个数异或偶数次就没了,异或奇数次就是本身。

那么我们不妨去考虑一个\(F(l, r)\)这个过程最终\(a_l, \cdots, a_r\)这每个数最终异或了多少次。

打表发现如下规律:

  • \(r - l + 1\)为偶数那么每个数异或次数都是偶数
  • 否则,\(l, l + 2, l + 4, \cdots\)这些位置上的数异或次数是奇数,其它位置上的数异或次数为偶数

    那么只需要维护两个序列,一个是奇数位置上的前缀和,一个是偶数位置上的前缀和,就可以做了。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define N 100010
int n, q, a[N]; struct SEG {
int t[N << 2];
void init() {
memset(t, 0, sizeof t);
}
void update(int id, int l, int r, int pos, int x) {
if (l == r) {
t[id] = x;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(id << 1, l, mid, pos, x);
else update(id << 1 | 1, mid + 1, r, pos, x);
t[id] = t[id << 1] ^ t[id << 1 | 1];
}
int query(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) {
return t[id];
}
int mid = (l + r) >> 1;
int x = 0;
if (ql <= mid) x ^= query(id << 1, l, mid, ql, qr);
if (qr > mid) x ^= query(id << 1 | 1, mid + 1, r, ql, qr);
return x;
}
}seg[2]; int main() {
int T; scanf("%d", &T);
for (int kase = 1; kase <= T; ++kase) {
printf("Case #%d:\n", kase);
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
seg[0].init(); seg[1].init();
for (int i = 1; i <= n; ++i) {
seg[i % 2].update(1, 1, n, i, a[i]);
}
int op, x, y;
while (q--) {
scanf("%d%d%d", &op, &x, &y);
switch(op) {
case 0 :
seg[x % 2].update(1, 1, n, x, y);
break;
case 1 :
if ((y - x + 1) % 2 == 0) puts("0");
else {
printf("%d\n", seg[x % 2].query(1, 1, n, x, y));
}
break;
}
}
}
return 0;
}

G. Winner

题意:

有一种游戏,三个模式,每个玩家在每个模式中都有一个力量值。现在要进行\(n - 1\)轮游戏,每一轮要挑出两个未被淘汰的人选择一种模式进行决斗,

在那个模式中,力量值小的人会被淘汰,直到最后剩下一个人。

数据保证每种模式中不同玩家的力量值不同。

现在上帝可以选择每一轮游戏参与决斗的两人(未被淘汰的),以及游戏模式,现在询问在上帝的操作下,第\(x\)个人是否可以成为最后留下来的人?

思路:

我们考虑先排个序,然后用\((a, b, c)\)表示要成为最后留下来的人的三种模式的力量的最小值,如果有人有某种大于等于这个三元组中对应的那个,那么就可以用它的其他两个属性去更新这个三元组。

按从大到小的顺序去更新即可。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define N 100010
#define pii pair <int, int>
#define fi first
#define se second
int n, q;
struct node {
int a, b, c;
node() {}
node(int a, int b, int c) : a(a), b(b), c(c) {}
bool operator <= (const node &other) const {
return a <= other.a || b <= other.b || c <= other.c;
}
}p[N];
pii a[N], b[N], c[N]; void Min(node &x, node y) {
x.a = min(x.a, y.a);
x.b = min(x.b, y.b);
x.c = min(x.c, y.c);
} int main() {
while (scanf("%d%d", &n, &q) != EOF) {
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i].fi);
a[i].se = i;
p[i].a = a[i].fi;
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &b[i].fi);
b[i].se = i;
p[i].b = b[i].fi;
}
for (int i = 1; i <= n; ++i) {
scanf("%d", &c[i].fi);
c[i].se = i;
p[i].c = c[i].fi;
}
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
sort(c + 1, c + 1 + n);
node T = node(1e9, 1e9, 1e9);
Min(T, p[a[n].se]);
Min(T, p[b[n].se]);
Min(T, p[c[n].se]);
for (int i = n - 1; i >= 1; --i) {
int pos = a[i].se;
if (T <= p[pos]) {
Min(T, p[pos]);
}
pos = b[i].se;
if (T <= p[pos]) {
Min(T, p[pos]);
}
pos = c[i].se;
if (T <= p[pos]) {
Min(T, p[pos]);
}
}
int x;
while (q--) {
scanf("%d", &x);
puts(T <= p[x] ? "YES" : "NO");
}
}
return 0;
}

H. Another Sequence

题意:

给出一个长度为\(n\)的序列\(a_i\)和另一个长度为\(n\)的序列\(b_i\),现在\(c_i\)的生成方式如下:

  • \(c_i\)的长度为\(n * n\)
  • \(c_{i * (n - 1) + j} = a_i | b_j\)
  • 将\(c_i\)升序排序

    现在要求支持两种操作:
  • 将\(c_i\)中\([l, r]\)区间内的数开根
  • 询问\(c_x\)的数值。

思路:

  • 对于\(c_i\)数组,我们肯定不能直接生成它,但是注意到\(1 \leq a_i, b_i \leq 10^5\),那么\(c_i\)中数的种类不会超过\(2 \cdot 10^5\),那么我们可以用\(FWT_or\)处理出每个数的个数
  • 那么对于询问,我们只需要处理出\(x\)位置上的数被开根了多少次,然后找到原本\(x\)位置上的数是什么即可
  • 我们考虑\(x\)位置上的数被开根了多少次?即在它前面出现的操作\(1\)中,\(l \leq x\)的区间个数减去\(r < x\)的区间个数
  • 考虑怎么找到\(x\)位置上的数是什么,维护一个前缀和,表示前\(i\)个数的出现次数,然后直接二分。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 200010
int n, q, a[N], b[N];
ll c[N], d[N];
ll H[N << 1]; int tot;
int res[N];
vector <vector<ll>> vec;
struct node {
ll x, y;
node() {}
node (ll x, ll y) : x(x), y(y) {}
}qrr[N]; void FWT(ll *a, int len, int mode) {
for (int i = 1; i < len; i <<= 1) {
for (int p = i << 1, j = 0; j < len; j += p) {
for (int k = 0; k < i; ++k) {
if (mode == 1) a[i + j + k] = (a[j + k] + a[i + j + k]);
else a[i + j + k] = (a[i + j + k] - a[j + k]);
}
}
}
} struct BIT {
int a[N];
void init() {
memset(a, 0, sizeof a);
}
void update(int x, int v) {
for (; x < N; x += x & -x) {
a[x] += v;
}
}
int query(int x) {
int res = 0;
for (; x > 0; x -= x & -x) {
res += a[x];
}
return res;
}
}bit[2]; int id(ll x) {
return lower_bound(H + 1, H + 1 + tot, x) - H;
} int main() {
int len = 1;
while (len < 100000) len <<= 1;
vec.clear(); vec.resize(len + 1);
vec[0].push_back(0); vec[1].push_back(1);
for (int i = 2; i <= len; ++i) {
int it = i;
vec[i].push_back(it);
while (it) {
it = floor(sqrt(it));
vec[i].push_back(it);
if (it == 1) break;
} }
while (scanf("%d", &n) != EOF) {
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= n; ++i) scanf("%d", b + i);
memset(c, 0, sizeof c);
memset(d, 0, sizeof d);
for (int i = 1; i <= n; ++i) ++c[a[i]], ++d[b[i]];
FWT(c, len, 1);
FWT(d, len, 1);
for (int i = 0; i < len; ++i) c[i] = c[i] * d[i];
FWT(c, len, -1);
for (int i = 0; i < len; ++i) c[i] += c[i - 1];
scanf("%d", &q);
tot = 0;
ll x, y;
for (int i = 1; i <= q; ++i) {
scanf("%lld%lld", &x, &y);
H[++tot] = x;
H[++tot] = y;
qrr[i] = node(x, y);
res[i] = -1;
}
sort(H + 1, H + 1 + tot);
tot = unique(H + 1, H + 1 + tot) - H - 1;
bit[0].init(); bit[1].init();
for (int i = 1; i <= q; ++i) {
if (qrr[i].x == 0) {
int del = bit[0].query(id(qrr[i].y)) - bit[1].query(id(qrr[i].y) - 1);
int num = lower_bound(c + 1, c + len, qrr[i].y) - c;
del = min(del, (int)vec[num].size() - 1);
res[i] = vec[num][del];
} else {
bit[0].update(id(qrr[i].x), 1);
bit[1].update(id(qrr[i].y), 1);
}
}
for (int i = 1; i <= q; ++i) if (res[i] != -1) printf("%d\n", res[i]);
}
return 0;
}

I. Hamster Sort

题意:

给出一个排列\(p_i\), 给定一种排序操作:

  1. 选择一个\(k\),令\(p_k = 1\),\(T = 1\)
  2. 然后遍历序列\(a_i\),寻找\(p_k\)
  3. 如果找到了,就令\(p_k = p_k + k\),遍历指针往后挪一个位置,然后回到步骤\(2\)
  4. 如果没有找到,就令\(T = T + 1\),然后遍历指针指向序列的开头,回到步骤\(2\)

现在要支出两种操作:

  • 交换\(p_x\)和\(p_y\)
  • 给出\(k\),询问上述排序操作的\(T\)是多少

思路:

  • 考虑\(k > \sqrt{n}\)的时候,我们可以直接暴力跳,不会跳超过\(\sqrt{n}\)次
  • 考虑\(k \leq \sqrt{n}\)的时候,\(k\)的取值只有\(\sqrt{n}\)种,可以直接预处理答案,交换的时候注意一下\(a_x\)和\(a_y\)都是同一个\(k\)的情况的贡献不要多加或者多减
  • 预处理答案的时候把\(1\)的贡献剔除出来,最后再加上即可

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define N 200010
int n, q, S;
int a[N], id[N];
int f[N]; int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d", &n); S = min(n - 1, 200);
for (int i = 1; i <= n; ++i) scanf("%d", a + i), id[a[i]] = i;
memset(f, 0, sizeof f);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= S; ++j) {
if (a[i] > 1 && (a[i] - 1) % j == 0) {
if (id[a[i] - j] > i) {
++f[j];
}
}
}
}
scanf("%d", &q);
int op, x, y, k;
while (q--) {
scanf("%d", &op);
if (op == 1) {
scanf("%d%d", &x, &y);
if (x == y) continue;
if (x > y) swap(x, y);
for (int i = 1; i <= S; ++i) {
if ((a[x] - 1) % i == 0) {
int pre = a[x] - i;
if (pre >= 1 && id[pre] <= y && id[pre] > x) {
--f[i];
}
int nx = a[x] + i;
if (nx <= n && id[nx] <= y && id[nx] > x) {
++f[i];
}
}
}
for (int i = 1; i <= S; ++i) {
if ((a[y] - 1) % i == 0) {
int pre = a[y] - i;
if (pre >= 1 && id[pre] > x && id[pre] < y) {
++f[i];
}
int nx = a[y] + i;
if (nx <= n && id[nx] > x && id[nx] < y) {
--f[i];
}
}
}
swap(id[a[x]], id[a[y]]);
swap(a[x], a[y]);
} else {
scanf("%d", &k);
if (k <= S) {
printf("%d\n", f[k] + 1);
} else {
int res = 1;
int it = 1;
while (it + k <= n) {
if (id[it + k] < id[it]) ++res;
it += k;
}
printf("%d\n", res);
}
}
}
}
return 0;
}

J. Prefix

题意:

给出\(n\)个字符串\(s_i\):

  • 维护一个字符串集合,把这个字符串加进去。
  • 再将所有字符串替换成他们的\(|s_i|\)个前缀。

    定义一个字符串的困难度为:

\[\begin{eqnarray*}
\prod\limits_{i = 1}^{|str|} d_{str_i} \bmod m
\end{eqnarray*}
\]

现在询问每个原字符串在那个字符串集合中,有多少字符串是它的前缀,并且困难度比它高?

思路:

先将所有字符串\(Hash\),存入\(map\)。

然后遍历每个字符串的前缀,如果前缀的困难度比它本身高,那么就统计有字符串集合中有多少个这样的前缀。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 100010
#define ull unsigned long long
int n, m, d[30], a[N];
string s[N]; map<ull, int> mp; int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
while (cin >> n >> m) {
mp.clear();
for (int i = 0; i < 26; ++i) cin >> d[i];
for (int i = 1; i <= n; ++i) cin >> s[i];
for (int i = 1; i <= n; ++i) {
ull Hash = 0;
ull base = 31;
a[i] = 1;
for (auto it : s[i]) {
Hash += base * it;
++mp[Hash];
base *= 31;
a[i] = 1ll * a[i] * d[it - 'a'] % m;
}
}
for (int i = 1; i <= n; ++i) {
ull Hash = 0;
ull base = 31;
int tmp = 1;
ll res = 0;
for (int j = 0, len = s[i].size(); j < len - 1; ++j) {
Hash += base * s[i][j];
base *= 31;
tmp = 1ll * tmp * d[s[i][j] - 'a'] % m;
if (tmp > a[i]) res += mp[Hash];
}
printf("%lld%c", res, " \n"[i == n]);
}
}
return 0;
}

K. A Good Game

签到题。

代码:

view code
#include <bits/stdc++.h>
using namespace std; #define ll long long
#define N 100010
int n, m;
ll v[N]; int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lld", v + i);
v[i] += v[i - 1];
}
ll res = 0;
vector <ll> vec;
for (int i = 1, l, r; i <= m; ++i) {
scanf("%d%d", &l, &r);
vec.push_back(v[r] - v[l - 1]);
}
sort(vec.begin(), vec.end());
for (int i = 0; i < m; ++i) {
res += 1ll * (i + 1) * vec[i];
}
printf("%lld\n", res);
}
return 0;
}

The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest的更多相关文章

  1. The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest B、H

    比赛链接https://www.jisuanke.com/contest/3098?view=challenges B题 拉格朗日插值 题意  T组输入.一个n次多项式 f(x) ,每项的系数不知道, ...

  2. The 2019 ICPC China Nanchang National Invitational and International Silk-Road Programming Contest - F.Sequence(打表+线段树)

    题意:给你一个长度为$n$的数组,定义函数$f(l,r)=a_{l} \oplus a_{l+1} \oplus...\oplus a_{r}$,$F(l,r)=f(l,l)\oplus f(l,l+ ...

  3. The Preliminary Contest for ICPC China Nanchang National Invitational and International Silk-Road Programming Contest

    打网络赛 比赛前的准备工作要做好 确保 c++/java/python的编译器能用 打好模板,放在桌面 A. PERFECT NUMBER PROBLEM #include <cstdio> ...

  4. 2019The Preliminary Contest for ICPC China Nanchang National Invitational

    The Preliminary Contest for ICPC China Nanchang National Invitational 题目一览表 考察知识点 I. Max answer 单调栈+ ...

  5. 计蒜客 38229.Distance on the tree-1.树链剖分(边权)+可持久化线段树(区间小于等于k的数的个数)+离散化+离线处理 or 2.树上第k大(主席树)+二分+离散化+在线查询 (The Preliminary Contest for ICPC China Nanchang National Invitational 南昌邀请赛网络赛)

    Distance on the tree DSM(Data Structure Master) once learned about tree when he was preparing for NO ...

  6. 2019 The Preliminary Contest for ICPC China Nanchang National Invitational(A 、H 、I 、K 、M)

    A. PERFECT NUMBER PROBLEM 题目链接:https://nanti.jisuanke.com/t/38220 题意: 输出前五个完美数 分析: 签到.直接百度完美数输出即可 #i ...

  7. ICPC China Nanchang National Invitational -- D. Match Stick Game(dp)

    题目链接:https://nanti.jisuanke.com/t/38223 题意:有一堆火柴构成了一个加减法式子,你可以把火柴重新组合,要求数字个数和原来一样多,每个数字的位数和对应原数字位数一样 ...

  8. The Preliminary Contest for ICPC China Nanchang National Invitational I. Max answer (单调栈+线段树)

    题目链接:https://nanti.jisuanke.com/t/38228 题目大意:一个区间的值等于该区间的和乘以区间的最小值.给出一个含有n个数的序列(序列的值有正有负),找到该序列的区间最大 ...

  9. The Preliminary Contest for ICPC China Nanchang National Invitational I题

    Alice has a magic array. She suggests that the value of a interval is equal to the sum of the values ...

随机推荐

  1. Docker 学习笔记(三):数据、网络、系统权限、docker-compose

    一.Docker 数据管理 Docker 持久化数据有两种方式: 使用数据卷:更安全,和主机耦合度低 将主机的目录挂载到容器中:更方便,主机和容器可以很方便地交换数据. 数据卷相关的命令: docke ...

  2. javascript Ajax 学习

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! AJAX是asynchronousjavascript and XML的简写,就是异步的javascrip ...

  3. 20190705-记IIS发布.NET CORE框架系统之所遇

    新手在IIS上发布.NET CORE框架的系统之注意事项 序:本篇随笔是我的处子笔,只想记录自己觉得在系统发布过程中比较重要的步骤,一来,忝作自己的学习笔记,以备不时之需,二来,也希望可以帮助有需要的 ...

  4. Python 练习汇总

    1. Python练习_Python初识_day1 2. Python练习_Python初识_day2 3. Python练习_初识数据类型_day3 4. Python练习_数据类型_day4 5. ...

  5. js防抖节流

    防抖(debounce) 所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间. 防抖函数分为非立即执行版和立即执行版. 非立即执行版: 第一 ...

  6. 【vue开发】超简单的防止连续点击js指令方法

    vue防重复点击(指令实现) 快速点击按钮会重复多次调用接口,防止出现这样的情况 全局定义,方便调用 新建plugins.js export default { install (Vue) { // ...

  7. Go数据类型之基本数据类型

    不想沦为芸芸众生的人只需做一件事,便是对自己不再散漫:他应当听从良知的呼唤:“成为你自己!” ---尼采 1.整型 有符号整数类型:int8.int16.int32和int64 无符号整数类型:uin ...

  8. Linux下相关性能指标数据采集

    1)如何查看某个服务进程所占本机物理内存及百分比情况?可以使用Linux命令行查看,也可以使用脚本实现.如下以查看本机kube-nginx服务进程为例: [root@kevin ~]# pidof k ...

  9. Python3 acm基础输入输出

    案例一:输入字符串分割并转化成多个int数值 a, b= map(int, input().split()) try: while True: a, b= map(int, input().split ...

  10. 解密Redis持久化【翻译】

    本文来自Redis的作者,他在论坛看到大家对Redis持久化误解较大,所以写此文章论述持久化 写操作的流程 首先我们来看一下数据库在进行写操作时到底做了哪些事,主要有下面五个过程. 客户端向服务端发送 ...