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. golang开发:环境篇(五)实时加载工具gin的使用

    gin 工具是golang开发中非常有用且有效的工具,有效的提高了开发调试go程序的效率. 为什么要使用gin 我们知道golang是编译型语言,这就表示go程序的每次改动,如果需要查看改动结果都必须 ...

  2. Vue Prop属性(父to子)

    通过Prop向子组件传递数据 第一步父组件中 <template> <div id="app"> <Users :users="users& ...

  3. 在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题)

    原文:在论坛中出现的比较难的sql问题:38(字符拆分 字符串检索问题) 最近,在论坛中,遇到了不少比较难的sql问题,虽然自己都能解决,但发现过几天后,就记不起来了,也忘记解决的方法了. 所以,觉得 ...

  4. VS.NET(C#)--2.3良构的XHTML

    良构的XHTML 1.关闭所有标签 2.禁止标签嵌套 3.区分大小写 4.引号  所有属性值都要置于引号中 5.唯一的根元素<html></html> 6.保留字符 XML中五 ...

  5. C#插入时间

    //获取日期+时间 DateTime.Now.ToString(); // 2008-9-4 20:02:10 DateTime.Now.ToLocalTime().ToString(); // 20 ...

  6. Asp.Net进阶/管家模式+发布订阅模式:练习

    现在需要实现一个需求:我需要在一个窗体中发送一个信息,其余几个窗体都能同时接收到发送的消息. 1.界面:一个管家窗体,1个主窗体,2个订阅者窗体.其中管家窗体为启动窗体. 2.订阅:2个订阅窗体订阅主 ...

  7. 【转载】网站配置Https证书系列(三):IIS网站设置Http链接直接跳转Https安全连接

    Http链接请求是以明文的方式传输,在传输的过程中很容易被篡改数据,一个典型的例子就是运营商的网络劫持注入广告信息等,而Https请求则是安全加密的请求,报文数据以密文的形式进行传输.当IIS网站配置 ...

  8. iptables的nat规则骚操作

    水一枪 我对防火墙这块的认知是比较低的, 之前一直没怎么去用 最多的要么就是 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A OUT ...

  9. 小数据玩转Pyspark(2)

    一.客户画像 客户画像应用:精准营销(精准预测.个性化推荐.联合营销):风险管控(高风险用户识别.异常用户识别.高可疑交易识别):运营优化(快速决策.产品组合优化.舆情分析.服务升级):业务创新(批量 ...

  10. Spring之AOP原理、代码、使用详解(XML配置方式)

    Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理.AOP代码以及AOP使用三个方向来讲AOP.先给出一张AOP相关的结构图,可以放大查看. 一.Spring AOP 接口设计 1.P ...