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. 【Linux】一步一步学Linux——Linux发展史(01)

    目录 00. 目录 01. Linux概述 02. Linux简史 03. Linux主要特性 04. Linux之父 05. Linux相关术语 06. Linux其它 07. Linux应用领域 ...

  2. 杭电1253 超时的bfs...

    #include<stdio.h>#include<iostream>#include<string.h>#include<algorithm>usin ...

  3. jenkins pipeline中获取shell命令的标准输出或者状态

    //获取标准输出//第一种 result = sh returnStdout: true ,script: "<shell command>" result = res ...

  4. win7用驱动精灵安装了bcm94352ac蓝牙驱动后还是不能用蓝牙的解决方法

    驱动精灵安装了驱动后,设备管理器处显示Bluetooth USB,但是没法用蓝牙,找不到蓝牙图标,后来在华硕官方下载了win7的Broadcom 蓝牙驱动程序装上之后就好了

  5. WebSocket 的应用

    后面用到了再来做整理 链接地址:https://www.cnblogs.com/zhaof/p/9833614.html

  6. List<T> or IList<T>

      If you are exposing your class through a library that others will use, you generally want to expos ...

  7. werkzeug/routing.py-Map()源码解析

    Map类主要用来存储所有的url规则和一些配置参数的.其中有一些配置的值只存储在Map实例里,因为这些值影响着所有的规则,还有一些其他的默认规则可以被重写. 通过之前分析的add_url_rule源码 ...

  8. UI5-技术篇-SAPUI5创建自定义控件

    转载:https://www.nabisoft.com/tutorials/sapui5/creating-custom-controls-in-sapui5     https://sapui5.h ...

  9. SDcms1.8代码审计

    由于工作原因,分析了很多的cms也都写过文章,不过觉得好像没什么骚操作都是网上的基本操作,所以也就没发表在网站上,都保存在本地.最近突然发现自己博客中实战的东西太少了,决定将以前写的一些文章搬过来,由 ...

  10. Wechat alert

    企业微信号登录--注册企业号或者企业微信 添加子部门 部门添加成员 创建应用 需要接收告警的人员关注企业号 企业号已经被部门成员关注 企业号有一个可以发送消息的应用,一个授权管理员,可以使用应用给成员 ...