1. 程式人生 > >2017 ACM/ICPC Asia Regional Shenyang Online 記錄

2017 ACM/ICPC Asia Regional Shenyang Online 記錄

接下來 沒有 min pair gic 不下來 node long 建立

這場比賽全程心態爆炸……

開場腦子秀逗簽到題WA了一發。之後0貢獻。

前期狀態全無 H題想復雜了,寫了好久樣例過不去。

然後這題還是隊友過的……

後期心態炸裂,A題後綴數組理解不深,無法特判k = 1時的情況。

然後也沒有心思讀題了,心靜不下來。

比賽題目鏈接

Problem B

$ans = k(n - k + 1)$

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;

LL n, k;

int main(){

	while (~scanf("%lld%lld", &n, &k)){
		printf("%lld\n", k * (n - k + 1));
	}

	return 0;
}

Problem D

對原序列求一遍最長不下降子序列,計長度為x

同時求一遍最長不上升子序列,計長度為y

則 $max(x, y) + k >= n$ 時符合題意。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second


typedef long long LL;

const int N = 1e5 + 10;
int n, k;
int a[N], c[N], f[N];
int T, ans;

void update(int x, int val){
	for (; x <= 100001; x += x & -x) c[x] = max(c[x], val);
}

int query(int x){
	int ret = 0;
	for (; x; x -= x & -x) ret = max(ret, c[x]);
	return ret;
}


int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &k);
		rep(i, 1, n) scanf("%d", a + i);
		memset(c, 0, sizeof c);
		memset(f, 0, sizeof f);

		rep(i, 1, n){
			f[i] = query(a[i]) + 1;
			update(a[i], f[i]);
		}

		ans = 0 ;
		rep(i, 1, n) ans = max(ans, f[i]);
		memset(f, 0, sizeof f);
		memset(c, 0, sizeof c);	
		dec(i, n, 1){
			f[i] = query(a[i]) + 1;
			update(a[i], f[i]);
		}

		rep(i, 1, n) ans = max(ans, f[i]);
		if (ans + k >= n) puts("A is a magic array.");
		else puts("A is not a magic array.");
	}


	return 0;
}

Problem E

$ans = F(2x + 3) - 1$

其中F()為斐波那契數列

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const LL mod = 998244353;

struct Matrix{ LL arr[3][3];}  mul, num, unit;

LL k;

Matrix Mul(Matrix a, Matrix b){
	Matrix c;
	rep(i, 1, 2) rep(j, 1, 2){
		c.arr[i][j] = 0;
		rep(k, 1, 2) (c.arr[i][j] += (a.arr[i][k] * b.arr[k][j] % mod)) %= mod;
	}
	return c;
}

Matrix Pow(Matrix a, LL k){
	Matrix ret(unit); for (; k; k >>= 1, a = Mul(a, a)) if (k & 1) ret = Mul(ret, a); return ret;
}

int main(){

	mul.arr[1][1] = 1; mul.arr[1][2] = 1;
	mul.arr[2][1] = 1; mul.arr[2][2] = 0;

	rep(i, 1, 2) unit.arr[i][i] = 1;
	num.arr[1][1] = 1;
	num.arr[2][1] = 1;

	while (~scanf("%lld", &k)){
		Matrix P = Pow(mul, 2 * k + 1);
		Matrix c = Mul(P, num);
		printf("%lld\n", (c.arr[1][1] + mod - 1) % mod);
	}	

	return 0;
}

Problem H

體現我低下的智商的一道題……

這題還是走了不少彎路(看來我對樹型DP的理解還不深入)

我的方法是考慮兩種情況。

第一種情況是從某個點買入然後在以他為根的子樹中的某個結點賣出。

或者從某個點賣出然後在以他為根的子樹中的某個結點買入。

設這個點為x,以他為根的子樹中的某個結點為y。

則收益1 = $s[x] - s[y] + a[y] - a[x] = (s[x] - a[x]) - (s[y] - a[y])$

收益2 = $s[x] - s[y] + a[x] - a[y] = (s[x] + a[x]) - (s[y] + a[y])$

其中s[i]為根結點到i的路程長度。

對某個x,要使收益1最大,則$(s[y] - a[y])$要最小。

對某個x,要使收益2最大,則$(s[y] + a[y])$要最小。

那麽我們預處理出每個點這兩個值,建立ST表,

然後對每個點求出前序DFS序和後序DFS序,

區間查詢最小值。

那麽第一種情況就解決了。

第二種情況,枚舉每個點,求LCA為這個點的兩個點之間的最優解。

樹型DP就可以了。

以上是我在比賽的時候的想法(真的太麻煩了)

Code如下(比賽的時候還沒寫完,賽後稍稍fix了下就過了)

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 + 10;
const int A = 19;

int T, n;
int ti, ans;
int f[N][A], g[N][A], a[N], c[N], d[N], F[N], G[N], s[N], l[N], r[N], fl[N];
vector <PII> v[N];

void dfs(int x, int fa, int now){
	s[x] = now;
	l[x] = ++ti;
	for (auto cnt : v[x]){
		int u = cnt.fi, w = cnt.se;
		if (u == fa) continue;
		dfs(u, x, now + w);
	}
	r[x] = ti;
}	

void ST(){
	rep(i, 1, n) f[i][0] = c[fl[i]];
	rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
	rep(i, 1, n) g[i][0] = d[fl[i]];
	rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) g[i][j] = min(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
}

inline int solve_f(int l, int r){
	int k = (int)log2((double)(r - l + 1));
	return min(f[l][k], f[r - (1 << k) + 1][k]);
}

inline int solve_g(int l, int r){
	int k = (int)log2((double)(r - l + 1));
	return min(g[l][k], g[r - (1 << k) + 1][k]);
}


void ask(int x){
	ans = max(ans,  c[x] - solve_f(l[x], r[x]));
	ans = max(ans,  d[x] - solve_g(l[x], r[x]));
}

void dfs2(int x, int fa){
	F[x] = c[x];
	G[x] = d[x];
	for (auto cnt : v[x]){
		int u = cnt.fi;
		if (u == fa) continue;
		dfs2(u, x);
		F[x] = min(F[x], F[u]);
		G[x] = min(G[x], G[u]);
	}
}

void dp(int x, int fa){
	int m1 = 1 << 30;
	int m2 = 1 << 30;
	int yy = 0;
	for (auto cnt : v[x]){
		int u = cnt.fi;
		if (u == fa) continue;
		if (yy){
			ans = max(ans, 2 * s[x] - F[u] - m2);
			ans = max(ans, 2 * s[x] - G[u] - m1);
		}
		
		dp(u, x);
		m1 = min(m1, F[u]);
		m2 = min(m2, G[u]);
		yy = 1;	
		
	}
}

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d", &n);
		rep(i, 0, n + 1) v[i].clear();
		rep(i, 1, n) scanf("%d", a + i);
		rep(i, 2, n){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			v[x].push_back(MP(y, z));
			v[y].push_back(MP(x, z));
		}

		memset(s, 0, sizeof s);
		memset(l, 0, sizeof l);

		ti = 0;
		dfs(1, 0, 0);

		rep(i, 1, n) c[i] = s[i] + a[i];
		rep(i, 1, n) d[i] = s[i] - a[i];
		rep(i, 1, n) fl[l[i]] = i;

		ST();
		ans = 0;
		rep(i, 1, n) ask(i);

		memset(F, 0, sizeof F);
		memset(G, 0, sizeof G);

		dfs2(1, 0);
		dp(1, 0);
		printf("%d\n", ans);

	}
	return 0;
}

賽後想想,其實根本不需要ST表……

直接來個預處理,第一種情況就解決了

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)
#define MP		make_pair
#define fi		first
#define se		second

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5 + 10;
const int A = 19;

int T, n;
int ti, ans;
int f[N][A], g[N][A], a[N], c[N], d[N], F[N], G[N], s[N], l[N], r[N], fl[N];
vector <PII> v[N];

void dfs(int x, int fa, int now){
	s[x] = now;
	l[x] = ++ti;
	for (auto cnt : v[x]){
		int u = cnt.fi, w = cnt.se;
		if (u == fa) continue;
		dfs(u, x, now + w);
	}
	r[x] = ti;
}	

void ST(){
	rep(i, 1, n) f[i][0] = c[fl[i]];
	rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
	rep(i, 1, n) g[i][0] = d[fl[i]];
	rep(j, 1, 18) rep(i, 1, n) if ((i + (1 << j) - 1) <= n) g[i][j] = min(g[i][j - 1], g[i + (1 << (j - 1))][j - 1]);
}

inline int solve_f(int l, int r){
	int k = (int)log2((double)(r - l + 1));
	return min(f[l][k], f[r - (1 << k) + 1][k]);
}

inline int solve_g(int l, int r){
	int k = (int)log2((double)(r - l + 1));
	return min(g[l][k], g[r - (1 << k) + 1][k]);
}


void ask(int x){
	ans = max(ans,  c[x] - solve_f(l[x], r[x]));
	ans = max(ans,  d[x] - solve_g(l[x], r[x]));
}

void dfs2(int x, int fa){
	F[x] = c[x];
	G[x] = d[x];
	for (auto cnt : v[x]){
		int u = cnt.fi;
		if (u == fa) continue;
		dfs2(u, x);
		F[x] = min(F[x], F[u]);
		G[x] = min(G[x], G[u]);
	}
}

void dp(int x, int fa){
	int m1 = 1 << 30;
	int m2 = 1 << 30;
	int yy = 0;
	for (auto cnt : v[x]){
		int u = cnt.fi;
		if (u == fa) continue;
		if (yy){
			ans = max(ans, 2 * s[x] - F[u] - m2);
			ans = max(ans, 2 * s[x] - G[u] - m1);
		}
		
		dp(u, x);
		m1 = min(m1, F[u]);
		m2 = min(m2, G[u]);
		yy = 1;	
		
	}
}

int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d", &n);
		rep(i, 0, n + 1) v[i].clear();
		rep(i, 1, n) scanf("%d", a + i);
		rep(i, 2, n){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			v[x].push_back(MP(y, z));
			v[y].push_back(MP(x, z));
		}

		memset(s, 0, sizeof s);
		memset(l, 0, sizeof l);

		ti = 0;
		dfs(1, 0, 0);

		rep(i, 1, n) c[i] = s[i] + a[i];
		rep(i, 1, n) d[i] = s[i] - a[i];
		rep(i, 1, n) fl[l[i]] = i;

		ST();
		ans = 0;
		rep(i, 1, n) ask(i);

		memset(F, 0, sizeof F);
		memset(G, 0, sizeof G);

		dfs2(1, 0);
		dp(1, 0);
		printf("%d\n", ans);

	}
	return 0;
}

Problem J

最後這題完全讀不進去啊……

題目大意就是給出一棵樹,和若幹條路徑。

由於某些點不可經過,這些給出的路徑是走不通的。

也就是說如果他給出了路徑(u, v),那麽u就不能走到v,反過來也一樣。

求不可經過的點最少有幾個。

這道題要用到一個結論:

如果樹上兩條路徑有交集,

假設u1和u2分別為兩條路徑兩個端點的LCA,

一定存在u1屬於這個交集,或者u2屬於這個交集。

對於路徑(u, v),我們求出u和v的LCA——z。

然後對這p條路徑,我們以deep[z]為關鍵字排序。

deep[z]越大,排越前面。

接下來就是一個貪心的過程。

我們從前往後處理這些路徑,

如果當前這條路徑的u或v已經被標記,那麽直接跳過。

如果當前這條路徑的u或v都沒有被標記,

那麽我們標記以z為根的子樹的所有點。

然後對答案加1.

之前我們預處理出所有點的前序DFS序l[i]和後序DFS序r[i],

若要標記以i為根的子樹的所有點,那麽給區間l[i]到r[i]打上標記即可。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 1e4 + 10;
const int A = 18;

int deep[N], c[N], l[N], r[N], sz[N], son[N], top[N], father[N];
int n, m, ti, ans;
vector <int> v[N];

struct node{
	int x, y, z;
	friend bool operator < (const node &a, const node &b){
		return deep[a.z] > deep[b.z];
	}
} path[N * 5];

void dfs(int x, int fa, int dep){
	father[x] = fa; 
	deep[x]   = dep;
	l[x]      = ++ti;
	sz[x]     = 1;

	for (auto u : v[x]){
		if (u == fa) continue;
		dfs(u, x, dep + 1);
		sz[x] += sz[u];
		if (sz[son[x]] < sz[u]) son[x] = u;
	}

	r[x] = ti;
}

void dfs2(int x, int fa, int tp){
	top[x] = tp;
	if (son[x]) dfs2(son[x], x, tp);

	for (auto u : v[x]){
		if (u == son[x] || u == fa) continue;
		dfs2(u, x, u);
	}
}

int LCA(int x, int y){
	for (; top[x] ^ top[y]; ){
		if (deep[top[x]] < deep[top[y]]) swap(x, y);
		x = father[top[x]];
	}

	return deep[x] > deep[y] ? y : x;
}

inline int update(int x, int y){
	for (; x <= n; x += x & -x) ++c[x]; ++y;
	for (; y <= n; y += y & -y) --c[y];
}

inline int query(int x){
	int ret = 0;
	for (; x; x -= x & -x) ret += c[x];
	return ret;
}

int main(){

	while (~scanf("%d", &n)){
		++n;
		rep(i, 0, n + 1) v[i].clear();


		ti = 0;
		rep(i, 2, n){
			int x, y;
			scanf("%d%d", &x, &y);
			++x; ++y;
			v[x].push_back(y);
			v[y].push_back(x);
		}
		

		memset(son, 0, sizeof son);
		dfs(1, 0, 0);
		dfs2(1, 0, 1);

		scanf("%d", &m);
		rep(i, 1, m){
			int x, y, z;
			scanf("%d%d", &x, &y);
			++x;
			++y;
			z = LCA(x, y);
			path[i] = node{x, y, z};
		}

		sort(path + 1, path + m + 1);
		memset(c, 0, sizeof c);

		ans = 0;
		rep(i, 1, m){
			int x = query(l[path[i].x]), y = query(l[path[i].y]);
			if (x || y) continue;
			update(l[path[i].z], r[path[i].z]);
			++ans;
		}

		printf("%d\n", ans);

	}

	return 0;
}

Problem L

從第1個點掃過去,掃到第x1個點的時候遊戲終止。

於是我們從x1+ 1接著掃,掃到遊戲終止時的那個點x2

繼續從x2 + 1開始掃。

周而復始,直到掃完為止。掃完一遍更新一次答案。

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

const int N = 1e6 + 10;
int n, l, r, ret, ans;
int a[N], b[N], c[N << 1], f[N << 1];

int main(){

	while (~scanf("%d", &n)){
		rep(i, 1, n) scanf("%d", a + i);
		rep(i, 1, n) scanf("%d", b + i);

		rep(i, 1, n) c[i] = a[i] - b[i];

		rep(i, 1, n) c[i + n] = c[i];
		rep(i, 1, n << 1) f[i] = f[i - 1] + c[i];
		l = 1; r = 1;

		c[1] = 0;
		rep(i, 1, n) c[i] = c[i - 1] + a[i];
		rep(i, 1, n) c[n + i] = c[n + i - 1] + a[i];

		ret = 0;
		while (true){
			while (r < 2 * n && r - l + 1 < n && f[r] - f[l - 1] >= 0) ++r;
			if (c[r] - c[l - 1] > ret){
				ret = c[r] - c[l - 1];
				ans = l - 1;
			}
			++r;
			l = r;
			if (l > 2 * n) break;
		}
		printf("%d\n", ans);
	}

	return 0;
}

2017 ACM/ICPC Asia Regional Shenyang Online 記錄