ABC372

andysj發表於2024-10-26

D

題目大意:

\(n\)座建築排成一排,每座建築的高度為\(h_i\)\(\forall i \in [1,n]\),找出滿足下麵條件的\(j\)的數量:

  • 在建築\(i\)\(j\)中,沒有建築比\(j\)高的
  • \(j \in [i+1,n]\)

\(n\leq 2 \times 10^5\)\({h}\)\(1\)\(n\)的排列。

分析:

考慮\(i\)不好處理,我們改為考慮每個\(j\)可以貢獻到哪些\(i\):即若\(h_j<h_i\)\(k\)\(j-1\)開始,一直向左,直到\(h_k>h_j\)時,\(i\in [k+1,j-1]\)都會被貢獻到(答案\(+1\))。所以,我們只需要快速找到在每個\(j\)往左第一個大於\(h_j\)的位置。這隻需要我們把所有的\(i\)按照\(h_i\)從大到小排序,然後用一個\(set\)維護即可。至於區間修改單點查詢,差分陣列即可處理。

程式碼:

#include <bits/stdc++.h>

using namespace std;

int n, t, pos[200005], d[200005], h[200005], c[200005];

set < int > a;

int ask(int x)
{
	auto p = a.lower_bound(x);
	if (p == a.end()) return max(1, *(prev(p)));
	return *(prev(p));
}

int main()
{
	scanf("%d", &n); a.insert(0);
	for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]), pos[h[i]] = i;
	for (int i = n; i >= 1; i -- )
	{
		int l = ask(pos[i]), r = pos[i] - 1;
		if (l == 0) l = 1;
		a.insert(pos[i]);
		if (l > r || r == 0) continue;
		d[l] ++ , d[r + 1] -- ;
	}
	for (int i = 1; i <= n; i ++ ) d[i] += d[i - 1], printf("%d ", d[i]);
	puts("");
	return 0;
}

F

題目大意:

\(n\)個點的有向圖共有\(n+m\)條邊。\(\forall i \in [1,n]\)\(i\)\(i+1\)存在一條邊(\(n+1\)對應\(1\))。\(\forall i \in [1,m]\)\(x_i\)\(y_i\)存在一條邊。求下列方案數:從點\(1\)開始,恰好走\(k\)步。答案對\(998244353\)取模。\(n,k\leq 2 \times 10^5,m\leq 50\)。無重邊自環。

分析:

\(O((n+m)k)\)的dp是顯然的,但是無法透過。注意到雖然有\(n+m\)條邊,但是其中的\(n\)條邊形式都是一致的,而\(m\)又很小,複雜度瓶頸在\(n\)。於是我們考慮固定這\(n\)條邊不動,讓\(n\)個點去動(即:某條邊\((i,i+1)\),把\(i+1\)當成\(i\),這樣就相當於自己轉移到自己,即保留原來的值,就不用考慮這條邊了)。設 \(d_i\) 為走到節點 \(i\) 的方案數。首先,為了處理方便,不妨將所有節點的標號減 \(1\),這樣旋轉操作可以轉化為標號取模操作(常用)。在將點整體移動的情況下,我們可以忽略對原環上邊的轉移。邊 \((x,y)\) 在第 \(t\) 次轉移時的實際效果是

\[d_{(y-t)\bmod n}\to d_{(y-t)\bmod n}+d_{(x-t+1)\bmod n} \]

模擬一下即可得到上述轉移。例如,\(n=8,(x,y)=(2,4),t=1\)時,實際轉移效果為\((2,3)\)。這是因為我們把\(i+1\)當成了\(i\)。另外,由於所有操作需要同時進行,所以需要快取值後再操作。

程式碼:

#include <bits/stdc++.h>

using namespace std;

const int mod = 998244353;

int n, m, k, add[55][2], x[55], y[55], dp[200005];

int main()
{
	scanf("%d%d%d", &n, &m, &k);
	for (int i = 1; i <= m; i ++ ) scanf("%d%d", &x[i], &y[i]), x[i] -- , y[i] -- ;
	dp[0] = 1;
	for (int i = 1; i <= k; i ++ )
	{
		for (int j = 1; j <= m; j ++ ) add[j][0] = (y[j] - i % n + n) % n, add[j][1] = dp[(x[j] - i % n + 1 + n) % n];
		for (int j = 1; j <= m; j ++ ) dp[add[j][0]] = (dp[add[j][0]] + add[j][1]) % mod;
	}
	int res = 0;
	for (int i = 0; i < n; i ++ ) res = (res + dp[i]) % mod;
	printf("%d\n", res);
	return 0;
}