1. 程式人生 > 其它 >2021,8,23 模擬賽

2021,8,23 模擬賽

目錄

前言:

\(T1\) 逆元。

\(T2\) 沒過大樣例竟然跑過去了 = =

\(T3\) 沒開 \(long ~long\) 含淚掛 0

期望: \(Soce: 20 + 60 + 10\)

實際: \(Soce: 20 + 100 + 0\)

T1 count

題面

一道 sb 數論題。

題目大意:

問有幾個無序二元組 \((x, y)\) 滿足 \(x y ≡ 1 \pmod p\), \(0 ≤ x < P, 0 ≤ y <P\)

solution

發現 \(xy \equiv 1(mod~p)\)

實際上就是 \(x\) 逆元的定義式。

\(x\) 有逆元當且僅當 \(gcd(x, p) = 1\) 的時候,並且逆元是唯一的。

\(1\sim P - 1\) 中與 \(P\) 互質的數有 \(s\) 個,考慮這 \(s\) 個數與它

們的逆元組成的二元組,這些二元組一定符合條件,那麼只要考

慮去重的問題。

可以發現如果 $xy \equiv 1(modp), x\neq y $那麼 \((x,y)\)\((y,x)\)

一定會在上述 s 個二元組中各出現一次,也就是被多算了一次。

\(x = y\) 的時候,\((x, x)\) 這個二元組只會出現一次,設個數為 \(t\)

個。

答案就是 \(\frac{s + t}{2}\)

\(1\sim P - 1\) 中與 \(P\) 互質的數的個數用尤拉函式來求就好。

code

/*
work by:Ariel_
Knowledge:
Time:
*/
#include <bits/stdc++.h>
#define ll long long
#define rg register
using namespace std;
int p, Ans;
int read(){
    int x = 0,f = 1; char c = getchar();
    while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    return x*f;
}
int phi(int m) {
   int res = m, tmp = m;
   for (int i = 2; i * i <= m; i++) {
   	   if (tmp % i == 0) {
   	      res = res / i * (i - 1);
		  while(tmp % i == 0) tmp /= i;
	   }
   }
   if (tmp > 1) res = res / tmp * (tmp - 1);
   return res;
}
int main(){
	freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
	p = read();
	Ans += phi(p);
	for(int i = 1; i <= p; i++) 
	if(i * i % p == 1) Ans++;
	Ans = Ans / 2;
	printf("%d\n", Ans);
	return 0;
}

T2 color (CF547D Mike and Fish)

一道 CF 原題

題面

solution

對於 \(x\) 座標相同的點,把這些點兩兩配對,如果剩下點則不管,然後在每一對點之間連一條邊。

對於 \(y\) 座標相等的點,把這些點兩兩配對,如果剩下點則不管,然後在每一對點之間連一條邊。

最後對得到的圖進行黑白染色可以得到我們的方案了。

證明:如果我們紅藍染色成功,那麼對於每一個 \(x\) 或者 \(y\) 座標來講最多剩一個點,這個點的顏色可以任意選擇。

code

/*
work by:Ariel_
Knowledge:
Time:
*/
#include <bits/stdc++.h>
#define rg register
using namespace std;
const int N = 500010;
int read(){
    int x = 0,f = 1; char c = getchar();
    while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    return x*f;
}
int n, x, y, col[N];
map<int, int>lx, ly;
int head[N], E;
char c[2];
struct edge{int v, nxt;}e[N << 1];
void add_edge(int u, int v) {
  e[++E] = (edge) {v, head[u]};
  head[u] = E;
}
void dfs(int u, int f){
   for(int i = head[u]; i; i = e[i].nxt) {
   	  int v = e[i].v;
   	  if (v == f) continue;
   	  if(col[v] == -1) {
   	     col[v] = col[u] ^ 1;
		 dfs(v, u);	
	  }
   }
}
int main(){
   //freopen("color.in", "r", stdin);
   //freopen("color.out", "w", stdout);
   n = read(); 
   memset(col, -1, sizeof col);
   c[0] = 'r', c[1] = 'b';
   for (int i = 1; i <= n; i++) {
   	  x = read(), y = read();
   	  if(lx[x]) {
   	  	add_edge(lx[x], i), add_edge(i, lx[x]);
	    lx[x] = 0;
	  } else lx[x] = i;
   	  if (ly[y]) {
   	  	add_edge(ly[y], i), add_edge(i, ly[y]);
		ly[y] = 0;
	  } else ly[y] = i;
   }
   for (int i = 1; i <= n; i++) 
   if(col[i] == -1) col[i] = 1, dfs(i, 0);
   for (int i = 1; i <= n; i++) printf("%c", c[col[i]]);
   puts("");
   return 0;
}
/*
10
1 1
2 1
2 2
3 2
4 3
4 4
3 3
2 3
2 4
1 3 
*/

T3 Sequence

很有價值的一道題。

題面

給出一個 \(n\) 個數的序列,一個區間的價值是區間內最小值乘最大值的積。

\(n\leq 10^5\)

考試的時候一直在考慮每個數對哪些區間有貢獻,把貢獻對應到二維平面上,求解。

寫資料結構寫多了吧 = = 沒注意到 \(n\leq 10^5\)

感覺題解解釋的就很詳細了,所以就直接搬來了。(其實是我懶

subtask1

\(O(n^2)\) 暴力

subtask2

資料是隨機的。

考慮分治。

如果當前是在考慮區間 \([l,r]\) 的子區間的價值和,設區間 \([l, r]\) 內最大值的下標為 \(mid\)

考慮左端點在 \([l, mid]\) 內,右端點在 \([mid,r ]\) 內的區間的價值和。它們內部的最大值是 \(a_{mid}\)

\([l, mid]\) 做字尾 \(\min\) ,對 \([mid, r]\) 做字首 \(\min\) ,不妨把結果記為\(mn[i]\) 。即 \(l\leq i\leq mid\) 時,\(mn[i] = min_{i = l}^{mid }a_i\)\(mid < i \leq r\)\(mn[i] = min_{i = mid}^r a_i\)

列舉左端點,由於右半部分的 \(mn\) 是單調的,一定存在一個分界點 \(p\) ,使得 \(j \leq p\) 時,\(mn[j] \geq mn[i]\)\(j > p\)\(mn[j] < mn[i]\) 。又由於左半部分的 \(mn\) 也是單調的,所以左端點 \(i\) 移動的時,分界點 \(p\) 的移動方向是不變的。這樣就容易對每個左端點 \(i\) 都求出分界點 \(p\) 了。另外再求出 \(mn\) 的字首和,就能 \(O(1)\) 求出左端點在 \(i\) , 右端點在 \([mid, r]\) 內的區間的價值和了。

然後遞迴計算區間 \([l, mid - 1]\)\([mid + 1, r]\)

時間複雜度 \(O(nlogn)\)

Subtask3

只有 10 種數,因此左端點固定時,最大值至多隻有 10 種,

即右端點在某段區間內的最大值相同,而這種區間最多隻有 10 個。

最小值同理。同時考慮最大值和最小值可以知道,左端點固定時,右端點在某段區間內的最大值和最小值都相同,而這種區間最多隻有 20 個。

移動左端點,用單調棧維護這些區間。複雜度 \(O(n \times 20)\)

Subtask5

Subtask2 的做法類似,每次取 $mid = \lfloor \frac{l + r}{2}\rfloor $,對最大值進行和最小值類似的操作(不妨把字首/字尾 \(\max\) 的結果記為 \(mx[i]\) )。我們可以求出最大值的分界點 \(q\)。除了 \(mn\) 的字首和外,再求出 \(mx\) 的字首和以及 \(mn \times mx\) 的字首和。\(p\)\(q\) 把右半部分劃分成三個區間,對三個區間分別計算。利用求出來的字首和可以 \(O(1)\) 算出右端點在一個區間內的價值和。

然後再遞迴計算左右兩半部分。

code

/*
work by:Ariel_
Knowledge:
Time:
*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#include <algorithm>
#define ll long long
#define rg register
using namespace std;
const ll mod = 998244353;
const int MAXN = 1e6 + 5;
int read(){
    int x = 0,f = 1; char c = getchar();
    while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
    while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
    return x*f;
}
int n, a[MAXN], Ans, mn[MAXN], mx[MAXN], s1[MAXN], s2[MAXN], s3[MAXN];
void work(int l, int r) {
   if (l == r) {
   	  Ans = (Ans + 1ll * a[l] * a[r] % mod) % mod;
   	  return ;
   }
   int mid = l + r >> 1;
   mn[mid] = a[mid];
   for (int i = mid - 1; i >= l; i--) mn[i] = min(mn[i + 1], a[i]);
   mx[mid] = a[mid];
   for (int i = mid - 1; i >= l; i--) mx[i] = max(mx[i + 1], a[i]);
   mn[mid + 1] = a[mid + 1];
   for (int i = mid + 2; i <= r; i++) mn[i] = min(mn[i - 1], a[i]);
   mx[mid + 1] = a[mid + 1];
   for (int i = mid + 2; i <= r; i++) mx[i] = max(mx[i - 1], a[i]);
   s1[mid] = 0;
   for (int i = mid + 1; i <= r; i++) s1[i] = (s1[i - 1] + mn[i]) % mod;
   s2[mid] = 0;
   for (int i = mid + 1; i <= r; i++) s2[i] = (s2[i - 1] + mx[i]) % mod;
   s3[mid] = 0;
   for (int i = mid + 1; i <= r; i++) s3[i] = (s3[i - 1] + 1ll * mx[i] * mn[i] % mod) % mod;
   int p1 = mid + 1, p2 = mid + 1;
   for (int i = mid; i >= l; i--) {
   	 while (p1 <= r && mn[p1] > mn[i]) p1++;
   	 while (p2 <= r && mx[p2] < mx[i]) p2++;
   	 if (p1 < p2) {
   	     Ans = (Ans + 1ll * mn[i] * mx[i] % mod * (p1 - 1 - mid) % mod) % mod;
	     Ans = (Ans + 1ll * mx[i] * (s1[p2 - 1] - s1[p1 - 1] + mod) % mod) % mod;
		 Ans = (Ans + (s3[r] - s3[p2 - 1] + mod) % mod) % mod;	
	  }
	  else {
	  	Ans = (Ans + 1ll * mn[i] * mx[i] % mod * (p2 - 1 - mid) % mod) % mod;
	  	Ans = (Ans + 1ll * mn[i] * (s2[p1 - 1] - s2[p2 - 1] + mod) % mod) % mod;
	  	Ans = (Ans + (s3[r] - s3[p1 - 1] + mod) % mod) % mod;
	  }
   }
   work(l, mid), work(mid + 1, r);
}
int main(){
   n = read();
   for (int i = 1; i <= n; i++) a[i] = read();
   work(1, n);
   printf("%d", Ans);
   puts("");
   return 0;
}