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. 史上最全最新java面试题合集二(附答案)

    下面小编整理了本套java面试题全集,分享给大家,希望对大家的java学习和就业面试有所帮助. 51.类ExampleA继承Exception,类ExampleB继承ExampleA. 有如下代码片断 ...

  2. Selenium 调用IEDriverServer打开IE浏览器

    Selenium 调用IEDriverServer打开IE浏览器 2016年03月30日 09:49:37 标签: selenium 14836 Selenium 调用IEDriverServer打开 ...

  3. Stack Overflow是如何做应用缓存的

    首先要说下缓存是什么?缓存,就是在取出数据结果后,暂时将数据存储在某些可以快速存取的位置(例如各种NoSQL如Redis,HBase,又或MemoryCache等等),于是就可以让这些耗时的数据结果多 ...

  4. TODO-依赖注入与控制反转

    交互框架之Actor与Listener的关系 https://www.cnblogs.com/mq0036/p/7473371.html

  5. deploy KBA 2167993

    The default trace shows the following error: ****************************************** Unable to cr ...

  6. Python 虚拟空间的使用

    使用虚拟环境, 可以将当前项目所使用的依赖与电脑中其他 Python 项目的依赖区分开, 避免依赖版本不匹配带来的问题, 同时也可以防止项目依赖被不当更新. mkdir myproject cd my ...

  7. Android笔记(十一) Android中的布局——网格布局

    网格布局是Android4.0新增的布局管理器,因此需要在Android4.0之后的版本才可以使用,之前的平台使用该布局的话,需要导入相应的支持库. GridLayout的作用类似于HTML中的tab ...

  8. 新一代纳秒级高带宽仿真工具平台——HAC Express

          HAC Express是基于FPGA的模型仿真开发环境,专注于高精度建模和超高速实时仿真,弥补了传统仿真工具平台无法进行纳秒级仿真的短板.         HAC系列自推出以来,经历了从v ...

  9. python3 jieba分词

    一.jieba库用于分词,https://github.com/fxsjy/jieba 二.分词:分词精细:全局(文本分析)<精确(快速成词)<搜素(搜素引擎分词) #分词 str=r'今 ...

  10. Windows 对外开放端口号

    前记 今天在做 Kafka 消息传输时,本地连接服务器的 Kafka 出现问题.连接不上,想到新的服务器应该是防火墙关闭所致. 我呢,就用了最直接暴力的方法:关闭防火墙~~~~(哈哈哈) 问题是解决了 ...