Problem solving report of 2021CCPC four northeast provinces

Posted by vijayfreaks on Wed, 15 Dec 2021 05:30:50 +0100

A. Matrix

General meaning

You need to use [ 1 , n 2 ] [1, n^2] All numbers of [1,n2] to fill one n × n n \times n n × Matrix of n

definition a [ ] a[] a [], where a i a_i ai ， is the second in the matrix i i The minimum value in line i definition S = { a 1 , a 2 , . . . , a n } ∩ { 1 , 2 , . . . , n } S = \{ a_1, a_2, ..., a _n\} \cap \{ 1, 2, ..., n\} S={a1​,a2​,...,an​}∩{1,2,...,n}.

We need to calculate for all cases ∑ ∣ S ∣ \sum|S| ∑∣S∣. last m o d    998244353 \mod998244353 mod998244353 outputs the answer

Problem solving ideas

thinking

We consider that the number range that can produce contribution is [ 1 , n ] [1, n] [1,n], so we might as well calculate the contribution of each number, and then add it

Assuming current considerations m m m, then for m m m we can start from n n Select a row from the n rows, and for the remaining n − 1 n - 1 n − 1 numbers, should be greater than m m m, so the choice is C n 2 − m n − 1 C_{n^2 - m}^{n - 1} Cn2 − mn − 1, and the numbers of this line can be arranged arbitrarily. The situation is n ! n! n!. The remaining numbers can be arranged arbitrarily ( n 2 − n ) ! (n^2 - n)! (n2−n)!

So eventually m m m's contribution is: n × C n 2 − m n − 1 × n ! × ( n 2 − n ) ! n \times C_{n^2 - m}^{n - 1} \times n! \times (n^2 - n)! n×Cn2−mn−1​×n!×(n2−n)!

(PS: teammates also came up with another way to calculate, formula: n 2 × ( n 2 − m ) ! × A n 2 − n m − 1 n^2 \times (n^2 - m)! \times A_{n^2-n}^{m-1} n2×(n2−m)!×An2−nm−1​)

We just need to enumerate m ∈ [ 1 , n ] m \in [1, n] m ∈ [1,n], and then accumulate the results

AC code

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 5E3 + 10, M = N * N, mod = 998244353;
int fpow(int a, int b) {
ll res = 1; a %= mod;
while (b) {
if (b & 1) res = res * a % mod;
b >>= 1;
a = 1ll * a * a % mod;
}
return res;
}

int num[M], innum[M];
void init(int n = M - 5)
{
num[0] = innum[0] = 1;
for (int i = 1; i <= n; ++i) {
num[i] = 1ll * num[i - 1] * i % mod;
}
innum[n] = fpow(num[n], mod - 2) % mod;
for (int i = n - 1; i >= 1; i--) {
innum[i] = 1ll * innum[i + 1] * (i + 1) % mod;
}
}
int C(int a, int b) { return 1ll * num[a] * innum[a - b] % mod * innum[b] % mod; }
int main()
{
init();

int t; scanf("%d", &t);
while (t--) {
ll n; scanf("%lld", &n);

ll res = 0;
rep(i, n) {
res = (res + n * C(n * n - i, n - 1) % mod * num[n] % mod * num[n * n - n]) % mod;
}
printf("%lld\n", res);
}

return 0;
}


C. Vertex Deletion

https://blog.csdn.net/qq_49494204/article/details/120112398

D. Lowbit

General meaning

Given a length of n n Sequence of n a [ ] a[] a [], there are two operations:

1 l r for [ l , r ] [l, r] Each number of [l,r] a i a_i ai, add it all l o w b i t ( a i ) lowbit(a_i) lowbit(ai​).

2 l r query [ l , r ] [l, r] Interval sum of [l,r] intervals

Problem solving ideas

Segment tree

First of all, after reading this topic, we can easily think that this is the interval operation of line segment tree

Considering the complex modification operations, compared with the usual interval modification, adding lowbit to each position seems to be impossible to maintain directly through calculation

So we thought x + l o w b i t ( x ) x + lowbit(x) Properties of x+lowbit(x) We found that if x ∈ { 2 of whole second power } The entire power of x \in \{2 \} The entire power} of x ∈ {2 is equivalent to each operation, x = x × 2 x = x \times 2 x=x×2.
Let's consider that for a number x x For x, it will add at most l o g x logx logx times l o w b i t ( x ) lowbit(x) lowbit(x), so that x ′ ∈ { 2 of whole second power } X '\ in \ {integral power of 2 \} The integral power of x ′∈ {2}

Therefore, we conclude that for positions that are not an integral power of 2, we carry out violent modification, and the total complexity of violent modification is O ( n l o g ∣ a i ∣ ) O(nlog|a_i|) O(nlog∣ai​∣). For the position that is the integral power of 2, we can multiply the interval by 2

The information to be maintained in the tree: interval sum, whether the elements in the interval are the integral power of 2, and interval multiplication

AC code

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E5 + 10, mod = 998244353;

int lowbit(int x) { return x & -x; }
bool judge(int x) { return bitset<32>(x).count() == 1; }

int w[N];
struct node {
int l, r;
ll sum; bool flag;
int lazy;
}t[N << 2];
void pushdown(node& op, int lazy) {
op.lazy = 1ll * op.lazy * lazy % mod;
op.sum = (op.sum * lazy) % mod;
}
void pushdown(int x) {
if (t[x].lazy == 1) return;
pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
t[x].lazy = 1;
}

void pushup(int x) {
t[x].sum = (t[x << 1].sum + t[x << 1 | 1].sum) % mod;
t[x].flag = t[x << 1].flag and t[x << 1 | 1].flag;
}

void build(int l, int r, int x = 1) {
t[x] = { l, r, w[l], 0, 1 };
if (l == r) { t[x].flag = judge(w[l]); return; }
int mid = l + r >> 1;
build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
pushup(x);
}

void modify(int l, int r, int x = 1) {
if (l <= t[x].l and r >= t[x].r) {
if (t[x].flag) { //The intervals are all integral powers of 2, multiplied directly by 2
pushdown(t[x], 2);
return;
}

if (t[x].l == t[x].r) {
t[x].sum += lowbit(t[x].sum);
t[x].flag = judge(t[x].sum);
return;
}
}
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
if (l <= mid) modify(l, r, x << 1);
if (r > mid) modify(l, r, x << 1 | 1);
pushup(x);
}

ll ask(int l, int r, int x = 1) {
if (l <= t[x].l and r >= t[x].r) return t[x].sum;
pushdown(x);
int mid = t[x].l + t[x].r >> 1;
ll res = 0;
if (l <= mid) res = ask(l, r, x << 1);
if (r > mid) res = (res + ask(l, r, x << 1 | 1)) % mod;
return res;
}
int main()
{
int T; cin >> T;
while (T--) {
int n; scanf("%d", &n);
rep(i, n) scanf("%d", &w[i]);
build(1, n);

int m; scanf("%d", &m);
while (m--) {
int tp, l, r; scanf("%d %d %d", &tp, &l, &r);
if (tp == 1) modify(l, r);
else printf("%lld\n", ask(l, r));
}
}

return 0;
}


E. Easy Math Problem

General meaning

Given number a a a. Ask if you can find one b b b. Make b b b yes a a Multiple of a, and b b b can be expressed as b = x + y + z b = x + y + z b=x+y+z, where x , y , z x, y, z x. Y, Z is b b Different factors of b

Problem solving ideas

order b = 6 a b = 6a b=6a, then b = a + 2 a + 3 a b = a + 2a + 3a b=a+2a+3a.

AC code

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
int main(void)
{
int t; cin >> t;
while (t--) {
ll n; scanf("%lld", &n);
n *= 6;
printf("%lld 3\n", n);
printf("%lld %lld %lld\n", n / 6, n / 3, n / 2);
}
return 0;
}


I. Takeaway

No, it's too difficult

K. City

General meaning

give n n n points, m m m undirected edges, q q q questions

Each edge has a weight w w w. Delete ownership value less than p p After the edge of p, the number of nodes that can reach each other in all the remaining nodes in the graph

Problem solving ideas

thinking

Firstly, considering that all edges are undirected, if a block is connected, all nodes in the block can reach each other The resulting contributions are: C n 2 , n by block large Small C_ N ^ 2, where n is the block size Cn2, n is the block size

Therefore, for each query, I know that I need to know how many connected blocks there are and the size of each connected block

Concurrent query + offline query

We consider using union search set to maintain connected block information
Because it is q q q queries, if we maintain the connected block information every time, the complexity will be O ( m q l o g n ) O(mqlogn) O(mqlogn).

We consider taking queries offline and sorting them from large to small Initially, it was considered that the graph was only n n Empty graph of n points, set the current query as p i p_i pi, we put all the edge weights w i ≥ p i w_i \ge p_i Edges with wi ≥ pi ， are added to the graph

If we sort from small to large, we should initially think that m edges exist in the graph, and each time we delete less than p i p_i pi ， the edge of the Because it is not easy to delete the merge set, we consider sorting from large to small

Consider how to maintain the added answer contribution s u m sum sum:

First, for the initial empty graph, s u m = 0 sum = 0 sum=0. When we merge two connected blocks a a a and b b b, we can first subtract the contribution of the original two connected blocks, and then add the contribution of the combined new connected blocks

AC code

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef pair<int, int> PII;
typedef long long ll;
const int N = 1E5 + 10;
int p[N], cou[N];
int find(int x) { return x == p[x] ? x : p[x] = find(p[x]); }

ll res[N * 2]; //So the 2E5 asked why the upper limit is only 1E5
ll sum = 0;
inline ll fact(ll x) { return x * (x - 1) / 2; } //Calculate C(n, 2)
void add(int a, int b) {
a = find(a), b = find(b);
if (a == b) return;
sum = sum - fact(cou[a]) - fact(cou[b]);
cou[a] += cou[b], p[b] = a;
sum += fact(cou[a]);
}
int main()
{
int t; cin >> t;
while (t--) {
sum = 0;
int n, m, q; scanf("%d %d %d", &n, &m, &q);
rep(i, n) p[i] = i, cou[i] = 1; //dsu init

map<int, vector<PII>, greater<>> edge; //Omit discretization
rep(i, m) {
int a, b, c; scanf("%d %d %d", &a, &b, &c);
edge[c].push_back({ a, b });
}

vector<PII> v;
rep(i, q) {
int k; scanf("%d", &k);
v.push_back({ k, i });
}
sort(v.begin(), v.end(), greater<>());

auto it = edge.begin();
for (auto& [val, id] : v) {
while (it != edge.end() and it->first >= val) {
for (auto& [a, b] : it->second) {
add(a, b);
}
++it;
}

res[id] = sum;
}

rep(i, q) printf("%lld\n", res[i]);
}

return 0;
}


M. Master of Shuangpin

General meaning

Simulated double spelling

Problem solving ideas

Simulate according to the requirements of the topic

AC code

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1005;
char s[N];

unordered_map<string, char> mp = {
{ "iu", 'q' }, { "ei", 'w' }, { "uan", 'r' }, { "ue", 't' },
{ "un", 'y' }, { "sh", 'u' }, { "ch", 'i' }, { "uo", 'o' },
{ "ie", 'p' }, { "ong", 's' }, { "iong", 's' }, { "ai", 'd' },
{ "en", 'f' }, { "eng", 'g' }, { "ang", 'h' }, { "an", 'j' },
{ "uai", 'k' }, { "ing", 'k' }, { "uang", 'l' }, { "iang", 'l' },
{ "ou", 'z' }, { "ia", 'x' }, { "ua", 'x' }, { "ao", 'c' },
{ "zh", 'v' }, { "ui", 'v' }, { "in", 'b' }, { "iao", 'n' },
{ "ian", 'm' }
};
int main()
{
for (char c = 'a'; c <= 'z'; ++c) {
string ss(1, c);
mp[ss] = c;
}

while (~scanf("%s", s + 1)) {
int n = strlen(s + 1);
if (n == 1) printf("%c%c", s[1], s[1]);
else if (n == 2) printf("%s", s + 1);
else {
string str = s + 1;
if (mp.count(str)) {
printf("%c%c", str[0], mp[str]);
}
else {
string a;
for (int i = 0; i < str.size() - 1; ++i) {
a += str[i];
string b = str.substr(i + 1);

if (mp.count(a) and mp.count(b)) {
printf("%c%c", mp[a], mp[b]);
break;
}
}
}
}
putchar(getchar());
}
return 0;
}