2017 ACM/ICPC Asia Regional Shenyang Online 记录

这场比赛全程心态爆炸……

开场脑子秀逗签到题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;
}
时间: 2024-10-11 13:02:52

2017 ACM/ICPC Asia Regional Shenyang Online 记录的相关文章

2017 ACM/ICPC Asia Regional Shenyang Online spfa+最长路

transaction transaction transaction Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others)Total Submission(s): 1496    Accepted Submission(s): 723 Problem Description Kelukin is a businessman. Every day, he travels arou

2017 ACM/ICPC Asia Regional Shenyang Online 部分刷题记录

cable cable cable 题意: M个灯,K个盒子,求最少要连多少条线,使任选K个盒子每个灯都能装下 思路: 每个灯要连(M-K+1)个 总共M*(M-K+1) happy happy happy 题意: 左右取数,孩子每次都去左右两边最大的那个,父亲想让孩子赢(大于父亲)且最小化分差 思路: 限时搜索,先dp预处理出l,r区间最大分差和最小分差,然后\(2^n\)搜索,但每进入一层都要利用dp数组更新一次答案 #include<bits/stdc++.h> #define pii

hdu6201 transaction transaction transaction(from 2017 ACM/ICPC Asia Regional Shenyang Online)

最开始一直想着最短路,不过看完题解后,才知道可以做成最长路.唉,还是太菜了. 先上图: 只要自己添加两个点,然后如此图般求最长路即可,emmm,用SPFA可以,迪杰斯特拉也可以,或者别的都ok,只要通过一次即可. 上代码: 1 #include <cstdio> 2 #include <cstring> 3 #include <vector> 4 #include <queue> 5 #include <algorithm> 6 using na

2017 ACM/ICPC Asia Regional Shenyang Online

cable cable cable Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2084    Accepted Submission(s): 1348 Problem Description Connecting the display screen and signal sources which produce differen

hdu6195 cable cable cable(from 2017 ACM/ICPC Asia Regional Shenyang Online)

最开始一直想不通,为什么推出这个公式,后来想了半天,终于想明白了. 题目大意是,有M个格子,有K个物品.我们希望在格子与物品之间连数量尽可能少的边,使得——不论是选出M个格子中的哪K个,都可以与K个物品恰好一一匹配. 然后你可以试着画图,每次必须有k个格子是单独的(与各物体只有一条线相连)所以还剩下m-k个格子,可以用来补位,也就是跟每个物品都相连,所以就有(m-k)*k 上代码(巨巨巨巨巨简单): 1 #include <cstdio> 2 #include <cstring>

2017 ACM/ICPC Asia Regional Shenyang Online array array array

2017-09-15 21:05:41 writer:pprp 给出一个序列问能否去掉k的数之后使得整个序列不是递增也不是递减的 先求出LIS,然后倒序求出最长递减子序列长度,然后判断去k的数后长度是否都大于所求长度 代码如下: #include <bits/stdc++.h> using namespace std; int arr1[100005],tmp1[100005],arr2[100005], tmp2[100005]; int len1,len2; int main() { in

HDU 6198(2017 ACM/ICPC Asia Regional Shenyang Online)

思路:找规律发现这个数是斐波那契第2*k+3项-1,数据较大矩阵快速幂搞定. 快速幂入门第一题QAQ #include <stdio.h> #include <stdlib.h> #include <cmath> #include <string.h> #include <iostream> #include <algorithm> #include <queue> #include <vector> #inc

HDU 6205(尺取法)2017 ACM/ICPC Asia Regional Shenyang Online

题目链接 emmmm...思路是群里群巨聊天讲这题是用尺取法.....emmm然后就没难度了,不过时间上3000多,有点.....盗了个低配本的读入挂发现就降到2800左右, 翻了下,发现神犇Claris280MS秒过.......%%% #include <stdio.h> #include <stdlib.h> #include <cmath> #include <string.h> #include <iostream> #include

2017 ACM/ICPC Asia Regional Shenyang Online:number number number hdu 6198【矩阵快速幂】

Problem Description We define a sequence F: ? F0=0,F1=1;? Fn=Fn?1+Fn?2 (n≥2). Give you an integer k, if a positive number n can be expressed byn=Fa1+Fa2+...+Fak where 0≤a1≤a2≤?≤ak, this positive number is mjf?good. Otherwise, this positive number is