Educational Codeforces Round 131 (Rated for Div. 2)刷题记录OR题解

题解

A Grass Field

题面翻译

给出一个 2 × 2 2 \times 2 2×2 的矩阵,矩阵的值都是 0 0 0 1 1 1,定义一次操作:选择一个点,将其所在的行和列的点的值全部修改为 0 0 0,求最少几次操作将整个矩阵修改为 0 0 0

by @Leonador

题目描述

There is a field of size 2 × 2 2 \times 2 2×2 . Each cell of this field can either contain grass or be empty. The value a i , j a_{i, j} ai,j is 1 1 1 if the cell ( i , j ) (i, j) (i,j) contains grass, or 0 0 0 otherwise.

In one move, you can choose one row and one column and cut all the grass in this row and this column. In other words, you choose the row x x x and the column y y y , then you cut the grass in all cells a x , i a_{x, i} ax,i and all cells a i , y a_{i, y} ai,y for all i i i from 1 1 1 to 2 2 2 . After you cut the grass from a cell, it becomes empty (i. e. its value is replaced by 0 0 0 ).

Your task is to find the minimum number of moves required to cut the grass in all non-empty cells of the field (i. e. make all a i , j a_{i, j} ai,j zeros).

You have to answer t t t independent test cases.

输入格式

The first line of the input contains one integer t t t ( 1 ≤ t ≤ 16 1 \le t \le 16 1t16 ) — the number of test cases. Then t t t test cases follow.

The test case consists of two lines, each of these lines contains two integers. The $ j $ -th integer in the $ i $ -th row is a i , j a_{i, j} ai,j . If a i , j = 0 a_{i, j} = 0 ai,j=0 then the cell ( i , j ) (i, j) (i,j) is empty, and if a i , j = 1 a_{i, j} = 1 ai,j=1 the cell ( i , j ) (i, j) (i,j) contains grass.

输出格式

For each test case, print one integer — the minimum number of moves required to cut the grass in all non-empty cells of the field (i. e. make all a i , j a_{i, j} ai,j zeros) in the corresponding test case.

样例 #1

样例输入 #1

3
0 0
0 0
1 0
0 1
1 1
1 1

样例输出 #1

0
1
2

解析

选择一行和一列,那么最多可以一次删除3个数,所以只需要1判断四个数的和就行了。 0 0 0输出 0 0 0, 4 4 4输出 2 2 2,其余都是 1 1 1

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5;
int n, m;
int a[N][N];
void solve()
{
	int cnt = 0;
	for(int i = 1; i <= 2; i ++)
	{
		for(int j = 1; j <= 2; j ++)
		{
			scanf("%d", &a[i][j]);
			if(a[i][j] == 1)
			{
				cnt ++;
			}
		}
	}
	if(cnt == 4)
	{
		printf("2\n");
	}
	else if(cnt < 4 && cnt > 0)
	{
		printf("1\n");
	}
	else printf("0\n");
}
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		solve();
	}
}

B Permutation

题目描述

Recall that a permutation of length n n n is an array where each element from 1 1 1 to n n n occurs exactly once.

For a fixed positive integer d d d , let’s define the cost of the permutation p p p of length n n n as the number of indices i i i ( 1 ≤ i < n ) (1 \le i < n) (1i<n) such that p i ⋅ d = p i + 1 p_i \cdot d = p_{i + 1} pid=pi+1 .

For example, if d = 3 d = 3 d=3 and p = [ 5 , 2 , 6 , 7 , 1 , 3 , 4 ] p = [5, 2, 6, 7, 1, 3, 4] p=[5,2,6,7,1,3,4] , then the cost of such a permutation is 2 2 2 , because p 2 ⋅ 3 = p 3 p_2 \cdot 3 = p_3 p23=p3 and p 5 ⋅ 3 = p 6 p_5 \cdot 3 = p_6 p53=p6 .

Your task is the following one: for a given value n n n , find the permutation of length n n n and the value d d d with maximum possible cost (over all ways to choose the permutation and d d d ). If there are multiple answers, then print any of them.

输入格式

The first line contains a single integer t t t ( 1 ≤ t ≤ 500 1 \le t \le 500 1t500 ) — the number of test cases.

The single line of each test case contains a single integer n n n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 2 \le n \le 2 \cdot 10^5 2n2105 ).

The sum of n n n over all test cases does not exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105 .

输出格式

For each test case, print the value d d d in the first line, and n n n integers in the second line — the permutation itself. If there are multiple answers, then print any of them.

样例 #1

样例输入 #1

2
2
3

样例输出 #1

2
1 2
3
2 1 3

解析

这道题就是最大化数组后一个数是前一个数的倍数的个数,那么可以直接贪心2倍是最多的。因为这道题不需要 d d d最大化(样例纯误导人)就算最大化答案不是 2 2 2就是 3 3 3,所以本代码的写法更为复杂且没用。直接从小到大枚举倍数模拟即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m;
bool st[N];
void solve()
{
	scanf("%d", &n);
	int maxv = 0;
	int w = log2(n);
	int r = 0, cnt = 0;
	if(n >= 3) 
	{
		r = 3, cnt = 1;
		while(r <= n)
		{
			r = r * 3;
			if(r <= n) cnt ++;
		}
	}
	else cnt = 0;
	//cout << w << " " << cnt << endl;
	int x = cnt;
	for(int i = 1; i <= n; i ++) st[i] = 0;
	if(x == w || w < x)
	{
		puts("3");
		printf("1 ");
		r = 3;
		while(r <= n)
		{
			st[r] = 1;
			printf("%d ", r);
			r = r * 3;
		}
		for(int i = 2; i <= n; i ++)
		{
			for(int j = i; j <= n; j *= 3)
			{
				if(st[j]) continue;
				else
				{
					st[j] = true;
					printf("%d ", j);
				}
			}
		}
	}
	else
	{
		puts("2");
		printf("1 ");
		for(int i = 2; i <= n; i ++)
		{
			for(int j = i; j <= n; j *= 2)
			{
				if(st[j]) continue;
				else
				{
					st[j] = true;
					printf("%d ", j);
				}
			}
		}
	}
	printf("\n");
}
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		solve();
	}
}

C Schedule Management

题面翻译

题意简述

n n n 个工人和 m m m 个任务,每个任务都有且仅有一个工人擅长做,如果让擅长做的工人去做,那么要花一个单位时间,否则要花两个单位时间。请问完成所有的任务至少要花多少时间。

注意:每项工作只能由一个工人完成,不能合作完成。

输入格式

本题多测,对于每组数据,第一行两个整数 n n n m m m。第二行 m m m 个数,表示每项工作所擅长的工人编号。

输出格式

每组数据一个整数表示最少要花的时间。

题目描述

There are n n n workers and m m m tasks. The workers are numbered from 1 1 1 to n n n . Each task i i i has a value a i a_i ai — the index of worker who is proficient in this task.

Every task should have a worker assigned to it. If a worker is proficient in the task, they complete it in 1 1 1 hour. Otherwise, it takes them 2 2 2 hours.

The workers work in parallel, independently of each other. Each worker can only work on one task at once.

Assign the workers to all tasks in such a way that the tasks are completed as early as possible. The work starts at time 0 0 0 . What’s the minimum time all tasks can be completed by?

输入格式

The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1t104 ) — the number of testcases.

The first line of each testcase contains two integers n n n and m m m ( 1 ≤ n ≤ m ≤ 2 ⋅ 1 0 5 1 \le n \le m \le 2 \cdot 10^5 1nm2105 ) — the number of workers and the number of tasks.

The second line contains m m m integers a 1 , a 2 , … , a m a_1, a_2, \dots, a_m a1,a2,,am ( 1 ≤ a i ≤ n 1 \le a_i \le n 1ain ) — the index of the worker proficient in the i i i -th task.

The sum of m m m over all testcases doesn’t exceed 2 ⋅ 1 0 5 2 \cdot 10^5 2105 .

输出格式

For each testcase, print a single integer — the minimum time all tasks can be completed by.

样例 #1

样例输入 #1

4
2 4
1 2 1 2
2 4
1 1 1 1
5 5
5 1 3 2 4
1 1
1

样例输出 #1

2
3
1
1

提示

In the first testcase, the first worker works on tasks 1 1 1 and 3 3 3 , and the second worker works on tasks 2 2 2 and 4 4 4 . Since they both are proficient in the corresponding tasks, they take 1 1 1 hour on each. Both of them complete 2 2 2 tasks in 2 2 2 hours. Thus, all tasks are completed by 2 2 2 hours.

In the second testcase, it’s optimal to assign the first worker to tasks 1 , 2 1, 2 1,2 and 3 3 3 and the second worker to task 4 4 4 . The first worker spends 3 3 3 hours, the second worker spends 2 2 2 hours (since they are not proficient in the taken task).

In the third example, each worker can be assigned to the task they are proficient at. Thus, each of them complete their task in 1 1 1 hour.

解析

这道题需要我们算出工人工作的最短时间,我们需要迅速反应出这道题的时间具有二分性。因为最短时间之前做不完,最短时间及以后都能做完,那么我们就需要去 O ( n ) O(n) O(n)检查是否做完就行了。假设有时间 t t t然后第 i i i擅长 a i a_i ai个工作,那么让 w = m i n ( t , a [ i ] ) w=min(t,a[i]) w=min(t,a[i]),如果 w = = a [ i ] w==a[i] w==a[i]那么还有空余时间,空余时间可以做 t − w 2 \frac{t-w}{2} 2tw个任务。否则还需要 ( t − w ) (t-w) (tw)的工作需要做完(注意不是时间)。然后其他同理,如果空余的任务数大于等于需要做完的任务,那么就可以做。代码实现较为容易,需要注意开 l o n g l o n g longlong longlong

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m;
int a[N];
 
bool check(ll t)
{
	ll re = 0;
	ll nt = 0;
 	for(int i = 1; i <= n; i ++)
	{
		ll w = min(t, 1ll * a[i]);
		if(w == a[i])
		{
			re += (t - w) / 2;
		}
		else
		{
			nt += (a[i] - w);
		}
	}
	//printf("%d %d %d\n", re, nt, t);
	if(re >= nt) return true;
	else return false;
}
void solve()
{
	scanf("%d %d", &n, &m);
	for(int i = 1; i <= n; i ++) a[i] = 0;
	for(int i = 1; i <= m; i ++)
	{
		int t;
		scanf("%d", &t);
		a[t] ++;
	}
	int l = 1, r = 2 * m;
	while(l < r)
	{
		int mid = l + r >> 1;
		if(check(mid)) r = mid;
		else l = mid + 1;
	}
	printf("%d\n", l);
}
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		solve();
	}
}

D Permutation Restoration

题目描述

Monocarp had a permutation a a a of n n n integers 1 1 1 , 2 2 2 , …, n n n (a permutation is an array where each element from $ 1 $ to $ n $ occurs exactly once).

Then Monocarp calculated an array of integers b b b of size n n n , where b i = ⌊ i a i ⌋ b_i = \left\lfloor \frac{i}{a_i} \right\rfloor bi=aii . For example, if the permutation a a a is [ 2 , 1 , 4 , 3 ] [2, 1, 4, 3] [2,1,4,3] , then the array b b b is equal to [ ⌊ 1 2 ⌋ , ⌊ 2 1 ⌋ , ⌊ 3 4 ⌋ , ⌊ 4 3 ⌋ ] = [ 0 , 2 , 0 , 1 ] \left[ \left\lfloor \frac{1}{2} \right\rfloor, \left\lfloor \frac{2}{1} \right\rfloor, \left\lfloor \frac{3}{4} \right\rfloor, \left\lfloor \frac{4}{3} \right\rfloor \right] = [0, 2, 0, 1] [21,12,43,34]=[0,2,0,1] .

Unfortunately, the Monocarp has lost his permutation, so he wants to restore it. Your task is to find a permutation a a a that corresponds to the given array b b b . If there are multiple possible permutations, then print any of them. The tests are constructed in such a way that least one suitable permutation exists.

输入格式

The first line contains a single integer t t t ( 1 ≤ t ≤ 1 0 5 1 \le t \le 10^5 1t105 ) — number of test cases.

The first line of each test case contains a single integer n n n ( 1 ≤ n ≤ 5 ⋅ 1 0 5 1 \le n \le 5 \cdot 10^5 1n5105 ).

The second line contains $ n $ integers b 1 , b 2 , … , b n b_1, b_2, \dots, b_n b1,b2,,bn ( 0 ≤ b i ≤ n 0 \le b_i \le n 0bin ).

Additional constrains on the input:

  • the sum of $ n $ over test cases does not exceed 5 ⋅ 1 0 5 5 \cdot 10^5 5105 ;
  • there exists at least one permutation a a a that would yield this array b b b .

输出格式

For each test case, print n n n integers — a permutation a a a that corresponds to the given array b b b . If there are multiple possible permutations, then print any of them.

样例 #1

样例输入 #1

4
4
0 2 0 1
2
1 1
5
0 0 1 4 1
3
0 1 3

样例输出 #1

2 1 4 3 
1 2 
3 4 2 1 5 
3 2 1

解析

这道题告诉我们 b b b数组我们需要通过转换求出一组排列 a a a使得满足对任意 ( 1 ≤ i ≤ n ) b i = ⌊ i a i ⌋ (1\le i \le n) b_i = \left\lfloor \frac{i}{a_i} \right\rfloor (1in)bi=aii。我们可以知道每一个 a i a_i ai的值范围。如果已一个数满足 b i = ⌊ i a i ⌋ b_i = \left\lfloor \frac{i}{a_i} \right\rfloor bi=aii那么 a i ∈ [ i b i + 1 + 1 , i b i ] a_i\in[\frac{i}{b_i+1}+1,\frac{i}{b_i}] ai[bi+1i+1,bii]。这样我们就可以在 O ( n ) O(n) O(n)的时间算出每一个位置的数的取值范围。现在问题就转化成为,给出 n n n个数的范围,然后找到一个合法的排列使得每一个关系都成立。这个问题是一个比较经典的问题,叫做活动安排。从直观上讲,应该是按照右边界作为第一关键字排序,做边界作为第二关键字。开一个 v e c t o r [ N ] vector[N] vector[N]来存以L开始的所有范围。然后枚举 1 1 1 n n n,把所有以 i i i开始的所有范围按照 [ R , p o s ] [R,pos] [R,pos]的格式存到优先队列当中,那么这样安排一定是最优的,因为它满足了我每一次分配的是“最需要”的节点。然后将 a n s [ p o s ] = i ans[pos]=i ans[pos]=i即可,然后输出所有答案。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
int n, m;
typedef pair<int,int> PII;
int b[N];
vector<PII> g[N];
priority_queue<PII,vector<PII>, greater<PII>> heap;
int a[N];
void solve()
{
	scanf("%d", &n);
	for(int i = 1; i <= n; i ++)
	{
		g[i].clear();
	}
	for(int i = 1; i <= n; i ++)
	{
		scanf("%d", &b[i]);
	}
	for(int i = 1; i <= n; i ++)
	{
		int L = i / (b[i] + 1) + 1;
		int R = b[i] == 0 ? n : i / b[i];
		g[L].push_back({R, i});
	}
	for(int i = 1; i <= n; i ++)
	{
		for(PII p:g[i]) heap.push(p);
		PII p = heap.top();
		heap.pop();
		a[p.second] = i;
	}
	for(int i = 1; i <= n; i ++)
	{
		printf("%d ", a[i]);
	}
	printf("\n");
}
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		solve();
	}
}

E Text Editor

题目描述

You wanted to write a text t t t consisting of m m m lowercase Latin letters. But instead, you have written a text s s s consisting of n n n lowercase Latin letters, and now you want to fix it by obtaining the text t t t from the text s s s .

Initially, the cursor of your text editor is at the end of the text s s s (after its last character). In one move, you can do one of the following actions:

  • press the “left” button, so the cursor is moved to the left by one position (or does nothing if it is pointing at the beginning of the text, i. e. before its first character);
  • press the “right” button, so the cursor is moved to the right by one position (or does nothing if it is pointing at the end of the text, i. e. after its last character);
  • press the “home” button, so the cursor is moved to the beginning of the text (before the first character of the text);
  • press the “end” button, so the cursor is moved to the end of the text (after the last character of the text);
  • press the “backspace” button, so the character before the cursor is removed from the text (if there is no such character, nothing happens).

Your task is to calculate the minimum number of moves required to obtain the text t t t from the text s s s using the given set of actions, or determine it is impossible to obtain the text t t t from the text s s s .

You have to answer $ T $ independent test cases.

输入格式

The first line of the input contains one integer T T T ( 1 ≤ T ≤ 5000 1 \le T \le 5000 1T5000 ) — the number of test cases. Then T T T test cases follow.

The first line of the test case contains two integers n n n and m m m ( 1 ≤ m ≤ n ≤ 5000 1 \le m \le n \le 5000 1mn5000 ) — the length of s s s and the length of t t t , respectively.

The second line of the test case contains the string s s s consisting of n n n lowercase Latin letters.

The third line of the test case contains the string t t t consisting of m m m lowercase Latin letters.

It is guaranteed that the sum of n n n over all test cases does not exceed 5000 5000 5000 ( ∑ n ≤ 5000 \sum n \le 5000 n5000 ).

输出格式

For each test case, print one integer — the minimum number of moves required to obtain the text t t t from the text s s s using the given set of actions, or -1 if it is impossible to obtain the text t t t from the text s s s in the given test case.

样例 #1

样例输入 #1

6
9 4
aaaaaaaaa
aaaa
7 3
abacaba
aaa
5 4
aabcd
abcd
4 2
abba
bb
6 4
baraka
baka
8 7
question
problem

样例输出 #1

5
6
3
4
4
-1

解析

这道题先看数据范围, n ≤ 5000 n\le5000 n5000时间复杂度就是 n 2 n^2 n2,根据学习动态规划的经验,有一道动态规划叫做编辑距离,这道题和本题非常相似,所以通过动态规划来解决这道题。这道题有几种操作,第一种是将光标移到左边一格,第二种是把光标移到右边一格,第三种是把光标移到最左边,第四种是把光标移到最右边,最后一种是删除光标前面的一段字符。光标一开始是在最右边,那么通过贪心的想法,删除操作肯定不包含将光标移到最右边,应该是从右删除到左,然后突然跳跃一段距离,然后从左往右删除,这样的删除顺序是最优的。所以我们需要考虑三种:从前往后删除,中间完全匹配,后面从后往前删除。那么我们可以假设 d p [ i ] [ j ] dp[i][j] dp[i][j] s s s串匹配了前 i i i为, t t t串匹配了前 j j j位时的操作次数。接下来分别讨论状态转移方程的书写,下标都是0开始,注意不能越界。

1. 1. 1.前缀:
初始状态 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1因为光标移到前面需要操作一次,其余都是正无穷。
d p [ i + 1 ] [ j ] = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j ] + 2 ) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 2) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+2) 未匹配上,需要光标右移一次,删除一次。
d p [ i + 1 ] [ j + 1 ] = m i n ( d p [ i + 1 ] [ j + 1 ] , d p [ i ] [ j ] + 1 ) ( s [ i ] = = t [ j ] ) dp[i + 1][j + 1]=min(dp[i + 1][j + 1], dp[i][j] + 1) (s[i] == t[j]) dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+1)(s[i]==t[j])匹配上,光标右移。
2. 2. 2.中缀
初始状态 d p [ 0 ] [ 0 ] = 0 dp[0][0]=0 dp[0][0]=0其余值不变。
d p [ i + 1 ] [ j + 1 ] = m i n ( d p [ i + 1 ] [ j + 1 ] , d p [ i ] [ j ] ) ( s [ i ] = = t [ j ] ) dp[i + 1][j + 1]=min(dp[i + 1][j + 1],dp[i][j]) (s[i]==t[j]) dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j])(s[i]==t[j])这一段直接跳过,所以操作数是0。
3. 3. 3.后缀
初始状态 d p [ 0 ] [ 0 ] = 0 dp[0][0]=0 dp[0][0]=0其余值不变。
d p [ i + 1 ] [ j ] = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j ] + 1 ) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+1) 未匹配上,需要光标删除一次。
d p [ i + 1 ] [ j + 1 ] = m i n ( d p [ i + 1 ] [ j + 1 ] , d p [ i ] [ j ] + 1 ) ( s [ i ] = = t [ j ] ) dp[i + 1][j + 1]=min(dp[i + 1][j + 1], dp[i][j] + 1) (s[i] == t[j]) dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+1)(s[i]==t[j])匹配上,光标左移。

如果 d p [ n ] [ m ] ≠ I N F , a n s = d p [ n ] [ m ] dp[n][m]\ne INF,ans = dp[n][m] dp[n][m]=INF,ans=dp[n][m],否则 a n s = − 1 ans=-1 ans=1

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, m;
int dp[N][N];
void solve()
{
	cin >> n >> m;
	string s, t;
	cin >> s >> t;
	for(int i = 0; i <= n; i ++)
	{
		for(int j = 0; j <= m; j ++) dp[i][j] = 2e9;
	}
	dp[0][0] = 1;
	for(int i = 0; i <= n; i ++)
	{
		for(int j = 0; j <= m; j ++)
		{
			if(i < n) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 2);
			if(s[i] == t[j] && i < n && j < m) dp[i + 1][j + 1] = min(dp[i + 1][j + 1], dp[i][j] + 1);
		}
	}
	dp[0][0] = 0;
	for(int i = 0; i < n; i ++)
	{
		for(int j = 0; j < m; j ++)
		{
			if(s[i] == t[j]) dp[i + 1][j + 1] = min(dp[i + 1][j + 1], dp[i][j]);
		}
	}
	dp[0][0] = 0;
	for(int i = 0; i <= n; i ++)
	{
		for(int j = 0; j <= m; j ++)
		{
			if(i < n) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + 1);
			if(s[i] == t[j] && i < n && j < m) dp[i + 1][j + 1] = min(dp[i + 1][j + 1], dp[i][j] + 1);
		}
	}
	if(dp[n][m] == 2e9)
	{
		printf("-1\n");
	}
	else
	{
		printf("%d\n", dp[n][m]);
	}
}
int main()
{
	int t;
	cin >> t;
	while(t --)
	{
		solve();
	}
}

F Points

题目描述

A triple of points i i i , j j j and k k k on a coordinate line is called beautiful if i < j < k i < j < k i<j<k and k − i ≤ d k - i \le d kid .

You are given a set of points on a coordinate line, initially empty. You have to process queries of three types:

  • add a point;
  • remove a point;
  • calculate the number of beautiful triples consisting of points belonging to the set.

输入格式

The first line contains two integers q q q and d d d ( 1 ≤ q , d ≤ 2 ⋅ 1 0 5 1 \le q, d \le 2 \cdot 10^5 1q,d2105 ) — the number of queries and the parameter for defining if a triple is beautiful, respectively.

The second line contains q q q integers a 1 , a 2 , … , a q a_1, a_2, \dots, a_q a1,a2,,aq ( 1 ≤ a i ≤ 2 ⋅ 1 0 5 1 \le a_i \le 2 \cdot 10^5 1ai2105 ) denoting the queries. The integer a i a_i ai denotes the i i i -th query in the following way:

  • if the point a i a_i ai belongs to the set, remove it; otherwise, add it;
  • after adding or removing the point, print the number of beautiful triples.

输出格式

For each query, print one integer — the number of beautiful triples after processing the respective query.

样例 #1

样例输入 #1

7 5
8 5 3 2 1 5 6

样例输出 #1

0
0
1
2
5
1
5

解析

这道题的要求是计算满足条件的triple的数量,假设 f [ i ] = [ i + 1 , i + d ] f[i] = [i + 1, i + d] f[i]=[i+1,i+d]的数字的数目,那么triple的个数 a n s = ( f [ i ] ∗ ( f [ i ] − 1 ) / 2 ans = (f[i] * (f[i]-1) / 2 ans=(f[i](f[i]1)/2,那么当插入一个数字x的时候,对于 [ x − d , x − 1 ] [x - d, x - 1] [xd,x1]这个区间的贡献是 1 1 1,反之是 − 1 -1 1,这道题的答案 a n s = ∑ ( f [ i ] A N D [ i 存在 ] ) ans = \sum(f[i] AND [i存在]) ans=(f[i]AND[i存在]),所以说需要使用线段树维护区间加减,然后需要维护当前的答案 a n s ans ans,所以还需要维护 i i i后面有几个数 w w w,加入k个数对答案的贡献是 1 2 ∗ ( ( f [ i ] + k ) 2 − ( f [ i ] + k ) ) − 1 2 ∗ ( f [ i ] 2 − f [ i ] ) = k ∗ f [ i ] + k ∗ ( k − 1 ) 2 \frac{1}{2} * ((f[i] + k) ^ 2 - (f[i] + k)) - \frac{1}{2} * (f[i] ^ 2 - f[i]) = k * f[i] +\frac{ k * (k - 1)}{2} 21((f[i]+k)2(f[i]+k))21(f[i]2f[i])=kf[i]+2k(k1),减去k个数对答案的贡献是 1 2 ∗ ( ( f [ i ] + k ) 2 − ( f [ i ] + k ) ) − 1 2 ∗ ( f [ i ] 2 − f [ i ] ) = k ∗ f [ i ] − k ∗ ( k − 1 ) 2 \frac{1}{2} * ((f[i] + k) ^ 2 - (f[i] + k)) - \frac{1}{2} * (f[i] ^ 2 - f[i]) = k * f[i] -\frac{ k * (k - 1)}{2} 21((f[i]+k)2(f[i]+k))21(f[i]2f[i])=kf[i]2k(k1)

所以线段数需要维护三个值,一个是答案,一个是 f [ i ] f[i] f[i]的和,一个是存在的数目。在修改的时候同时维护 a n s ans ans,每一次询问的答案就是 t r [ 1 ] . a n s tr[1].ans tr[1].ans,需要注意的是在维护区间和的时候,加上一个常数对一个父节点的贡献是儿子的数目*常数。所以需要维护节点个数 t o t tot tot。还有就是先维护区间和,然后再是单点修改,顺序不能换。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, m;
int st[N];
struct node
{
	ll sum, tot, ans;
	ll tag;
}tr[N * 4];

void update(int id)
{
	tr[id].ans = tr[id * 2].ans + tr[id * 2 + 1].ans;
	tr[id].sum = tr[id * 2].sum + tr[id * 2 + 1].sum;
	tr[id].tot = tr[id * 2].tot + tr[id * 2 + 1].tot;
}

void settag(int id,ll val)
{
	if(val > 0)
	{
		tr[id].ans += val * tr[id].sum;
		tr[id].ans += val * (val - 1) / 2 * tr[id].tot;
	}
	else
	{
		tr[id].ans += val * tr[id].sum;
		ll v = -val;
		tr[id].ans += v * (v + 1) / 2 * tr[id].tot;
	}
	tr[id].sum += val * tr[id].tot;
	tr[id].tag += val;
}


void pushdown(int id)
{
	if(tr[id].tag != 0)
	{
		settag(id * 2, tr[id].tag);
		settag(id * 2 + 1, tr[id].tag);
		tr[id].tag = 0;
	}
}

void change(int id,int l,int r,int pos, ll val, int tot)
{
	if(l == r)
	{
		tr[id].ans = val * (val - 1) / 2;
		tr[id].sum = val;
		tr[id].tot = tot;
	}
	else
	{
		int mid = l + r >> 1;
		pushdown(id);
		if(pos <= mid) change(id * 2, l, mid, pos, val, tot);
		else change(id * 2 + 1, mid + 1, r, pos, val, tot);
		update(id);
	}
}

void modify(int id,int l,int r,int ql,int qr,int val)
{
	if(l == ql && r == qr)
	{
		settag(id, val);
	}
	else
	{
		int mid = l + r >> 1;
		pushdown(id);
		if(qr <= mid) modify(id * 2, l, mid, ql, qr, val);
		else if(ql > mid) modify(id * 2 + 1, mid + 1, r, ql, qr, val);
		else
		{
			modify(id * 2, l, mid, ql, mid, val);
			modify(id * 2 + 1, mid + 1, r, mid + 1, qr, val);
		}
		update(id);
	}
}

int Query(int id,int l,int r,int ql,int qr)
{
	//cout << l << " " << r << " " << ql << " " << qr << endl;
	if(l == ql && r == qr)
	{
		return tr[id].tot;
	}
	else
	{
		int mid = l + r >> 1;
		pushdown(id);
		if(qr <= mid) return Query(id * 2, l, mid, ql, qr);
		else if(ql > mid) return Query(id * 2 + 1, mid + 1, r, ql, qr);
		else
		{
			return Query(id * 2, l, mid, ql, mid) + Query(id * 2 + 1, mid + 1, r, mid + 1, qr);
		}
		update(id);
	}
}

int main()
{
	int d;
	scanf("%d %d", &n, &d);
	for(int i = 1; i <= n; i ++)
	{
		int w;
		scanf("%d" ,&w);
 		if(st[w])
 		{
 			modify(1, 1, N - 5, max(w - d, 1), w, -1);
 			change(1, 1, N - 5, w, 0, 0);
 		}
		else
		{
			modify(1, 1, N - 5, max(w - d, 1), w, 1);
			ll res = Query(1, 1, N - 5, w, min(N - 5, w + d));
 			change(1, 1, N - 5, w, res, 1);
		}
		st[w] ^= 1;
		printf("%lld\n", tr[1].ans);
	}
	return 0;
}

评价

我认为这场比赛作为教育场十分具有教育意义。A,B题考查贪心,C考察二分,D考察经典模型,E题考察编辑距离为模型的动态规划问题。F则是线段树维护区间和的应用。可以说每道题都非常有意义,巩固了基础知识和数据结构,还训练了动态规划问题。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值