Codeforces Round #695 (Div. 2)

比賽地址c++

A (水題)

題目連接
⭐⭐⭐數組

題目:
給出\(n\)個面板,每一個面板初始時間相同,每過1s面板上數字會加1(數字在\(0\sim9\)循環播放),任意時刻選擇一個面板\(x\)使他的時間中止,其餘的面板\(y\)會在間隔\(|x-y|\)s之後中止,問從左到右順序組成的數字序列所對應的十進制數最大值是多少?spa

解析:
只要最高位最大便可,所以首先須要最高位爲9,那麼次高位必定得是8,再次高位並不是7,由於能夠在次高位爲8時中止第二個面板構成989的序列code

總結:get

  • \(n=1\)時,只輸出9
  • \(n\ne1\)時,輸出\(98\)其他位模十加1便可
#include<bits/stdc++.h>
using namespace std;

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		if (n == 1)
			printf("9\n");
		else
		{
			printf("98");
			int t = 8;
			for (int i = 2; i < n; ++i)
				printf("%d", t = (t + 1) % 10);
			printf("\n");
		}
	}
}

B (暴力)

題目連接
⭐⭐it

題目:
給出一個數組\(a\),能夠改變一次數組中某一元素的值,問數列\(a\)中最小的極值點個數是多少?class

解析:test

  1. 對於改變任意一個數而言,可以影響到的極值點的狀況也只有本身或者左右的兩個數據,而爲了減少極值點數量,必然是使\(a[i]=a[i-1]\mid a[i]=a[i+1]\)兩種狀況,
  2. 這樣即可以對已是極值點的數據進行操做,而且暴力檢查兩種狀況下縮減極值點數目的最大值便可

注意:能夠對端點值進行復制,避免在遍歷過程當中對端點進行特判循環

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5 + 5;
int dat[maxn];
int cnt;

bool check(int i)
{
	return dat[i] > dat[i - 1] && dat[i] > dat[i + 1] || dat[i] < dat[i - 1] && dat[i] < dat[i + 1];
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		cnt = 0;
		int n;
		scanf("%d", &n);
		for (int i = 1; i <= n; ++i)
			scanf("%d", &dat[i]);
		dat[0] = dat[1], dat[n + 1] = dat[n];
		int mx = 0;
		for (int i = 2; i < n; ++i)
		{
			if (check(i))
			{
				++cnt;
				int t = dat[i], now = 0, aa = 0;
				for (int j = i - 1; j <= i + 1; ++j)
					now += check(j);
				dat[i] = dat[i - 1];
				for (int j = i - 1; j <= i + 1; ++j)
					aa += check(j);
				mx = max(mx, now - aa);
				aa = 0;
				dat[i] = dat[i + 1];
				for (int j = i - 1; j <= i + 1; ++j)
					aa += check(j);
				mx = max(mx, now - aa);
				dat[i] = t;
			}
		}
		printf("%d\n", cnt - mx);
	}
}

C (貪心+思惟)

題目連接
⭐⭐遍歷

題目:
給出三個數列,每次能夠從兩個數列中分別挑出兩個數 \(a,b\) 並從原數列中刪去,並將\(a-b\)放進第一個數列,問最後剩餘的一個數最大是多少(其他兩個數列爲空)?

解析:
兩種貪心策略

  1. 每次選出某一個序列的最小值用這個最小值減去其餘序列的」全部「(保留一個)值,再用保留值減去這個所選的序列中全部的值,最後剩餘數的值=其餘兩個序列的值之和-2*選中序列的值
  2. 分別選出兩個序列中的最小值分別用各自的最小值減去另外一個序列的除最小值之外的其餘值,未選中序列的「全部」(保留一個)值被兩個最小值中任意一個減去均可以,而後再用保留值減去這兩個最小值進行了若干次操做後的結果,最後剩餘數的值=除兩個選中最小值的和-選中的最小值的和

總結:

\[result=sum-2\times\min(sum_{array},min_{ai},min_{aj}) \]

注意:要開long long

#include<bits/stdc++.h>
using namespace std;

LL mn[3] = { INF,INF,INF }, sum[3], t;
LL n[3];

int main()
{
	scanf("%d%d%d", &n[0], &n[1], &n[2]);
	for (int i = 0; i < 3; ++i)
	{
		while (n[i]--)
		{
			scanf("%lld", &t);
			sum[i] += t;
			mn[i] = min(mn[i], t);
		}
	}
	printf("%lld", sum[0] + sum[1] + sum[2] - 2 * min({ mn[0] + mn[1],mn[1] + mn[2],mn[0] + mn[2],sum[0],sum[1],sum[2] }));
}

D (dp)

題目連接
⭐⭐⭐⭐

題目:
給出一組數列,每次能夠選擇一個位置做爲起點,每次能夠向左(右)移動一步,共移動\(k\)次,給出\(q\)條詢問,每次改變數列中某個元素的值,問當前數列下全部可能路徑對應的權值和的和是多少?(答案取模\(10^9+7\)

解析:

  1. 首先想到的是對於給定的數組長度\(n\)以及移動次數\(k\),那確定對應數列中每個元素有一個對應的貢獻值,因而考慮打表找規律,發現找不着,再觀察到數據範圍控制在\(10^3\),因此聯想到用dp求解
  2. 若是定義狀態\(dp[i][j]\)爲以\(i\)爲終點,總共移動了\(j\)次對應的路徑數量,很顯然能夠獲得狀態轉移方程

\[dp[i][j]=dp[i-1][j-1]+dp[i+1][j-1] \]

 同時因爲路徑是具備對稱性的,也能夠將這個狀態理解爲,以\(i\)爲起點,總共移動了\(j\)次對應的路徑數量

  1. 那麼若是要求得移動\(j\)次後剛好在第\(i\)個數所對應的路徑數量,則爲\(dp[i][j]\times dp[i][k-j]\),因而也就能夠獲得全部路徑中通過\(i\)點的路徑數量

\[sum[i]=\sum_{j=0}^kdp[i][j]\times dp[i][k-j] \]

  1. 最後每次查詢的時候更新變化量便可

注意:

  1. 必定要記得最後查詢的時候,變化量可能爲負值,要加模取模
  2. 注意狀態轉移方程中,要先枚舉\(j\),再枚舉\(i\),由於只有在全部元素對應的\(j-1\)層的移動確認之後,才能夠更新\(j\)層的數值
#include<bits/stdc++.h>
typedef long long LL;
using namespace std;

int n, k, q;
LL sum[5005], dp[5005][5005], dat[5005];
const int mod = 1e9 + 7;

int main()
{
	scanf("%d%d%d", &n, &k, &q);
	for (int i = 1; i <= n; ++i)
	{
		dp[i][0] = 1;
		scanf("%lld", &dat[i]);
	}
	for (int j = 1; j <= k; ++j)
		for (int i = 1; i <= n; ++i)
			dp[i][j] = (dp[i - 1][j - 1] + dp[i + 1][j - 1]) % mod;
	LL ret = 0;
	for (int i = 1; i <= n; ++i)
	{
		for (int j = 0; j <= k; ++j)
			sum[i] = (sum[i] + dp[i][j] * dp[i][k - j]) % mod;
		ret = (ret + sum[i] * dat[i]) % mod;
	}
	while (q--)
	{
		scanf("%d%d", &n, &k);
		ret = (ret + sum[n] * (k - dat[n]) % mod + mod) % mod;
		dat[n] = k;
		printf("%lld\n", ret);
	}
}
相關文章
相關標籤/搜索