跳到主要内容

CF1600分刷题记录有意思的题目

Parsa's Humongous Tree

https://www.luogu.com.cn/problem/CF1528A

题面翻译

多组数据 (tt 组)

给你大小为 nn 的一棵树,ii 号节点有权值范围 [li,ri][l_i,r_i],让你对每个节点赋予一个权值 aia_i,使得每个节点权值都在规定的范围里并且对于每条边 (u,v)(u,v)auav\sum{|a_u-a_v|} 最大,并求出这个最大值。

2n105,n2×105,1liri1092\le n\le 10^5,\sum n\le 2\times 10^5,1\le l_i\le r_i\le10^9,时限 1s1\text s,空限 256MB256\text{MB}

Translated by @ZCPB

题目描述

Parsa has a humongous tree on nn vertices.

On each vertex vv he has written two integers lvl_v and rvr_v .

To make Parsa's tree look even more majestic, Nima wants to assign a number ava_v ( lvavrvl_v \le a_v \le r_v ) to each vertex vv such that the beauty of Parsa's tree is maximized.

Nima's sense of the beauty is rather bizarre. He defines the beauty of the tree as the sum of auav|a_u - a_v| over all edges (u,v)(u, v) of the tree.

Since Parsa's tree is too large, Nima can't maximize its beauty on his own. Your task is to find the maximum possible beauty for Parsa's tree.

输入格式

The first line contains an integer tt (1t250)(1\le t\le 250) — the number of test cases. The description of the test cases follows.

The first line of each test case contains a single integer nn (2n105)(2\le n\le 10^5) — the number of vertices in Parsa's tree.

The ii -th of the following nn lines contains two integers lil_i and rir_i (1liri109)(1 \le l_i \le r_i \le 10^9) .

Each of the next n1n-1 lines contains two integers uu and vv (1u,vn,uv)(1 \le u , v \le n, u\neq v) meaning that there is an edge between the vertices uu and vv in Parsa's tree.

It is guaranteed that the given graph is a tree.

It is guaranteed that the sum of nn over all test cases doesn't exceed 21052 \cdot 10^5 .

输出格式

For each test case print the maximum possible beauty for Parsa's tree.

样例输入 #1

3
2
1 6
3 8
1 2
3
1 3
4 6
7 9
1 2
2 3
6
3 14
12 20
12 19
2 12
10 17
3 17
3 2
6 5
1 5
2 6
4 6

样例输出 #1

7
8
62

The trees in the example:

In the first test case, one possible assignment is a={1,8}a = \{1, 8\} which results in 18=7|1 - 8| = 7 .

In the second test case, one of the possible assignments is a={1,5,9}a = \{1, 5, 9\} which results in a beauty of 15+59=8|1 - 5| + |5 - 9| = 8

思路

考虑树形DP:

显然,每个点取的aia_{i}一定是取边界liril_{i}和r_{i}的时候最有,

状态表示:f[i][0/1]f[i][0/1]表示以ii为根的子树中,选左边界(0)和选右边界(1)的最大值

状态转移:

  • 选左边界:f[x][0]+=max(f[y][0]+abs(l[x]l[y]),f[y][1]+abs(l[x]r[y]))f[x][0] += max(f[y][0] + abs(l[x] - l[y]), f[y][1] + abs(l[x] - r[y]))
  • 选右边界:f[x][1]+=max(f[y][0]+abs(r[x]l[y]),f[y][1]+abs(r[x]r[y]))f[x][1] += max(f[y][0] + abs(r[x] - l[y]), f[y][1] + abs(r[x] - r[y]))

即以x为根节点,如果选了左边界,那么他的所有儿子y可以选左边界,或者右边界,两者取最大值

结果:max(f[1][0],f[1][1])max(f[1][0],f[1][1])

注意:本题不开读入优化会TLE,如果忘记清空vector建图,会MLE。

代码

#include <bits/stdc++.h>
#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define cxk 1
#define debug(s, x) \
if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;
const int N = 1e5 + 10;
vector<int> e[N];
int f[N][2], l[N], r[N];

void dfs(int x, int fa) {
for (auto y : e[x]) {
if (y == fa) continue;
dfs(y, x);
f[x][0] += max(f[y][0] + abs(l[x] - l[y]), f[y][1] + abs(l[x] - r[y]));
f[x][1] += max(f[y][0] + abs(r[x] - l[y]), f[y][1] + abs(r[x] - r[y]));
}
}

void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) e[i].clear();
for (int i = 1; i <= n; i++) f[i][1] = f[i][0] = 0;
for (int i = 1; i <= n; i++) cin >> l[i] >> r[i];
for (int i = 1; i <= n - 1; i++) {
int x, y;
cin >> x >> y;
e[x].push_back(y);
e[y].push_back(x);
}
dfs(1, 0);
int t = max(f[1][0], f[1][1]);
cout << t << endl;
}
signed main() {
cin.tie(0), cout.tie(0);
ios::sync_with_stdio(false);
int _ = 1;
cin >> _;
while (_--) solve();
return 0;
}

Add One

题面翻译

称“将一个数上每一位的值+1+1”为一次操作,例如对于9393进行一次操作后的结果为104104。输入n,mn,m,输出对nn进行mm次操作的结果。

题目描述

You are given an integer nn . You have to apply mm operations to it.

In a single operation, you must replace every digit dd of the number with the decimal representation of integer d+1d + 1 . For example, 19121912 becomes 2102321023 after applying the operation once.

You have to find the length of nn after applying mm operations. Since the answer can be very large, print it modulo 109+710^9+7 .

输入格式

The first line contains a single integer tt ( 1t21051 \le t \le 2 \cdot 10^5 ) — the number of test cases.

The only line of each test case contains two integers nn ( 1n1091 \le n \le 10^9 ) and mm ( 1m21051 \le m \le 2 \cdot 10^5 ) — the initial number and the number of operations.

输出格式

For each test case output the length of the resulting number modulo 109+710^9+7 .

样例输入 #1

5
1912 1
5 6
999 1
88 2
12 100

样例输出 #1

5
2
6
4
2115

For the first test, 19121912 becomes 2102321023 after 11 operation which is of length 55 .

For the second test, 55 becomes 2121 after 66 operations which is of length 22 .

For the third test, 999999 becomes 101010101010 after 11 operation which is of length 66 .

For the fourth test, 8888 becomes 10101010 after 22 operations which is of length 44 .

思路

数位DP?

只需要一位一位的考虑每个数字,n中的数字之间是互不影响的,先进行dp预处理0-10的每个数字经过j次操作之后的位数

状态表示:f[i][j]f[i][j]表示当前的数字为ii,需要操作的次数为jj之后的长度 (i[0,9],j[0,2e5]i \in[0,9],j \in[0,2e5]

状态转移:

  • 如果i+j<10i+j<10,则数字还是一位 ,即 f[i][j]=1(i+j<10)f[i][j]=1 (i+j<10)
  • 如果i+j>=10i+j>=10 则产生进位,可以先用i10i-10个操作,使得这个数字变成1010,剩余的操作次数为i+j10i+j-10,即f[i][j]=f[1][i+j10]+f[0][i+j10]f[i][j]=f[1][i+j-10]+f[0][i+j-10]

注意:需要先枚举操作次数j,再枚举i,否则可能出现f[1][i+j10]f[0][i+j10]f[1][i+j-10]和f[0][i+j-10]没有计算的情况

读入需要优化,需要取模

代码

#include <bits/stdc++.h>
#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define cxk 1
#define debug(s, x) \
if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

const int N = 2e5 + 10, mod = 1e9 + 7;

int f[10][N];

void solve() {
string s;
int m;
cin >> s >> m;
int res = 0;
for (auto c : s) {
int t = f[c - '0'][m];
res = (res + f[c - '0'][m]) % mod;
}
cout << res << endl;
}

void init() {
for (int j = 0; j < N; j++) {
for (int i = 0; i <= 9; i++) {
if (i + j < 10) {
f[i][j] = 1;
} else {
f[i][j] = (f[0][i + j - 10] + f[1][i + j - 10]) % mod;
}
}
}
}
signed main() {
cin.tie(0), cout.tie(0);
ios::sync_with_stdio(false);
init();
int _ = 1;
cin >> _;
while (_--) solve();
return 0;
}

Distinct Characters Queries

题面翻译

一个字符串s,和q个操作(包括以下两个)

  • 1 pos c 将s[pos]上的字符变为c
  • 2 l r 输出[l,r]中有多少个不同的字符

题目描述

You are given a string ss consisting of lowercase Latin letters and qq queries for this string.

Recall that the substring s[l;r]s[l; r] of the string ss is the string slsl+1srs_l s_{l + 1} \dots s_r . For example, the substrings of "codeforces" are "code", "force", "f", "for", but not "coder" and "top".

There are two types of queries:

  • 1 pos c1~ pos~ c ( 1poss1 \le pos \le |s| , cc is lowercase Latin letter): replace sposs_{pos} with cc (set spos:=cs_{pos} := c );
  • 2 l r2~ l~ r ( 1lrs1 \le l \le r \le |s| ): calculate the number of distinct characters in the substring s[l;r]s[l; r] .

输入格式

The first line of the input contains one string ss consisting of no more than 10510^5 lowercase Latin letters.

The second line of the input contains one integer qq ( 1q1051 \le q \le 10^5 ) — the number of queries.

The next qq lines contain queries, one per line. Each query is given in the format described in the problem statement. It is guaranteed that there is at least one query of the second type.

输出格式

For each query of the second type print the answer for it — the number of distinct characters in the required substring in this query.

样例输入 #1

abacaba
5
2 1 4
1 4 b
1 5 b
2 4 6
2 1 7

样例输出 #1

3
1
2

样例输入 #2

dfcbbcfeeedbaea
15
1 6 e
1 4 b
2 6 14
1 7 b
1 12 c
2 6 8
2 1 6
1 7 c
1 2 f
1 10 a
2 7 9
1 10 a
1 14 b
1 1 f
2 1 11

样例输出 #2

5
2
5
2
6

思路

考虑使用STL中的set进行操作:,可以开26个set,分别存每个字母出现的下标

  • 单点修改:将原来字母的set里面的下标idx删去,然后修改s中的字母,再将修改后的字母对应的set插入坐标idx
  • 区间查询:由于每个字母最多在[l,r][l,r]区间中出现一次,因此可以遍历26个字母,找第一个>=l>=l的数(即坐标),如果可以找到并且<=r<=r,说明这个字母在区间[l,r][l,r]中出现了,对答案的贡献+1

代码

#include <bits/stdc++.h>
#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define cxk 1
#define debug(s, x) \
if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;
void solve() {
string s;
cin >> s;
int n = s.size();
s = " " + s;
vector<set<int>> pos(26);
for (int i = 1; i <= n; i++) {
pos[s[i] - 'a'].insert(i);
}
auto update = [&](int idx, char c) {
pos[s[idx] - 'a'].erase(idx);
s[idx] = c;
pos[c - 'a'].insert(idx);
};

auto query = [&](int l, int r) {
int res = 0;
for (int i = 0; i < 26; i++) {
auto it = pos[i].lower_bound(l);
if (it != pos[i].end() && *it <= r) res++;
}
return res;
};
int q;
cin >> q;
while (q--) {
int op;
cin >> op;
if (op == 1) {
int idx;
char c;
cin >> idx >> c;
update(idx, c);
} else {
int l, r;
cin >> l >> r;
cout << query(l, r) << endl;
}
}
}
signed main() {
int _ = 1;
while (_--) solve();
return 0;
}

Kuroni and Impossible Calculation

https://www.luogu.com.cn/problem/CF1305C

题面翻译

  • 给定 nn 个整数 a1,...,na_{1,...,n} 和一个整数 mm
  • 你需要求出 1i<jnaiajmodm\prod\limits_{1\le i<j\le n} |a_i-a_j| \bmod m 的值。
  • 2n2×1052\le n\le 2\times 10^51m10001 \le m \le 10000ai1090 \le a_i \le 10^9

题目描述

To become the king of Codeforces, Kuroni has to solve the following problem.

He is given nn numbers a1,a2,,ana_1, a_2, \dots, a_n . Help Kuroni to calculate 1i<jnaiaj\prod_{1\le i<j\le n} |a_i - a_j| . As result can be very big, output it modulo mm .

If you are not familiar with short notation, 1i<jnaiaj\prod_{1\le i<j\le n} |a_i - a_j| is equal to a1a2a1a3|a_1 - a_2|\cdot|a_1 - a_3|\cdot \dots a1ana2a3a2a4\cdot|a_1 - a_n|\cdot|a_2 - a_3|\cdot|a_2 - a_4|\cdot \dots a2an\cdot|a_2 - a_n| \cdot \dots an1an\cdot |a_{n-1} - a_n| . In other words, this is the product of aiaj|a_i - a_j| for all 1i<jn1\le i < j \le n .

输入格式

The first line contains two integers nn , mm ( 2n21052\le n \le 2\cdot 10^5 , 1m10001\le m \le 1000 ) — number of numbers and modulo.

The second line contains nn integers a1,a2,,ana_1, a_2, \dots, a_n ( 0ai1090 \le a_i \le 10^9 ).

输出格式

Output the single number — 1i<jnaiajmodm\prod_{1\le i<j\le n} |a_i - a_j| \bmod m .

样例输入 #1

2 10
8 5

样例输出 #1

3

样例输入 #2

3 12
1 4 5

样例输出 #2

0

样例输入 #3

3 7
1 4 9

样例输出 #3

1

In the first sample, 85=33mod10|8 - 5| = 3 \equiv 3 \bmod 10 .

In the second sample, 141545=341=120mod12|1 - 4|\cdot|1 - 5|\cdot|4 - 5| = 3\cdot 4 \cdot 1 = 12 \equiv 0 \bmod 12 .

In the third sample, 141949=385=1201mod7|1 - 4|\cdot|1 - 9|\cdot|4 - 9| = 3 \cdot 8 \cdot 5 = 120 \equiv 1 \bmod 7 .

思路

注意到m很小,只有1000,

n>mn>m的时候,根据鸽笼原理,一定会有两个数a[i]a[j]a[i]和a[j]它们的模mm的结果是相同的,即a[i]==a[j](modm)a[i]==a[j](\mod m),则一定会存在一项abs(a[i]a[j])modm=0abs(a[i]-a[j])\mod m=0,此时答案就为0

n<=mn<=m的时候,n<=1000n<=1000,因此暴力算即可。

代码

#include <bits/stdc++.h>
#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) \
if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;
void solve() {
int n, m;
cin >> n >> m;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
if (n > m) {
cout << 0 << endl;
} else {
int res = 1;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
int t = abs(a[i] - a[j]) % m;
res = (res * t) % m;
}
}
cout << res << endl;
}
}
signed main() {
IOS int _ = 1;
while (_--) solve();
return 0;
}

Obtain The String

题面翻译

给定两个字符串 sstt。有一个初始为空的字符串 zz,每次操作可以往 zz 的末尾加上一个 ss 的子序列,问至少经过多少次操作才能使 zz 变成 tt,如果不可能输出 1-1

题目描述

You are given two strings ss and tt consisting of lowercase Latin letters. Also you have a string zz which is initially empty. You want string zz to be equal to string tt . You can perform the following operation to achieve this: append any subsequence of ss at the end of string zz . A subsequence is a sequence that can be derived from the given sequence by deleting zero or more elements without changing the order of the remaining elements. For example, if z=acz = ac , s=abcdes = abcde , you may turn zz into following strings in one operation:

  1. z=acacez = acace (if we choose subsequence aceace );
  2. z=acbcdz = acbcd (if we choose subsequence bcdbcd );
  3. z=acbcez = acbce (if we choose subsequence bcebce ).

Note that after this operation string ss doesn't change.

Calculate the minimum number of such operations to turn string zz into string tt .

输入格式

The first line contains the integer TT ( 1T1001 \le T \le 100 ) — the number of test cases.

The first line of each testcase contains one string ss ( 1s1051 \le |s| \le 10^5 ) consisting of lowercase Latin letters.

The second line of each testcase contains one string tt ( 1t1051 \le |t| \le 10^5 ) consisting of lowercase Latin letters.

It is guaranteed that the total length of all strings ss and tt in the input does not exceed 21052 \cdot 10^5 .

输出格式

For each testcase, print one integer — the minimum number of operations to turn string zz into string tt . If it's impossible print 1-1 .

样例输入 #1

3
aabce
ace
abacaba
aax
ty
yyt

样例输出 #1

1
-1
3

思路

遇到这种字符串的问题,可以不用去想位置上面是哪个数,而考虑哪个数出现在哪些位置。

显然本题如果没有t里面的字符就是无解,否则一定有解。

记录上一个字符出现的位置为last

那考虑当前字符的时候,只需要在对应的pos数组里面去二分找>last的最小位置即可,如果找不到,答案就加1,并且last设置为当前数组的第一个坐标。

代码

#include <bits/stdc++.h>

#define int long long
#define yes cout << "YES" << endl;
#define no cout << "NO" << endl;
#define IOS cin.tie(0), cout.tie(0), ios::sync_with_stdio(false);
#define cxk 1
#define debug(s, x) if (cxk) cout << "#debug:(" << s << ")=" << x << endl;
using namespace std;

void solve() {
string s, t;
cin >> s >> t;
vector<int> pos[200];
for (int i = 0; i < s.size(); ++i) pos[s[i]].push_back(i);
int last = -1;
int res = 1;
for (int i = 0; i < t.size(); ++i) {
char c = t[i];
if (pos[c].empty()) {
cout << -1 << endl;
return;
}
int idx = std::upper_bound(pos[c].begin(), pos[c].end(), last) - pos[c].begin();
if (idx == pos[c].size()) {
res++;
last = pos[c].front();
} else {
last = pos[c][idx];
}
}
cout << res << endl;

}

signed main() {
IOS
#ifndef ONLINE_JUDGE
freopen("../test.in", "r", stdin);
freopen("../test.out", "w", stdout);
#endif
int _ = 1;
cin >> _;
while (_--) solve();
return 0;
}