8VC Venture Cup 2016 - Final Round (Div. 2 Edition)

时间:2021-10-18 06:03:42

暴力 A - Orchestra

import java.io.*;
import java.util.*; public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner (System.in);
int r = cin.nextInt ();
int c = cin.nextInt ();
int n = cin.nextInt ();
int k = cin.nextInt ();
int[][] a = new int[12][12];
for (int i=0; i<n; ++i) {
int x = cin.nextInt ();
int y = cin.nextInt ();
a[x][y] = 1;
}
int ans = 0;
for (int sx=1; sx<=r; ++sx) {
for (int lx=0; sx+lx<=r; ++lx) {
for (int sy=1; sy<=c; ++sy) {
for (int ly=0; sy+ly<=c; ++ly) {
int cnt = 0;
for (int i=sx; i<=sx+lx; ++i) {
for (int j=sy; j<=sy+ly; ++j) {
if (a[i][j] == 1) cnt++;
if (cnt >= k) break;
}
}
if (cnt >= k) ans++;
}
}
}
}
System.out.println (ans);
}
}

找规律 B - Island Puzzle

相对位置不变,0的位置任意,当做不存在.查看每个点到对应的点是否能一起移动到.

#include <bits/stdc++.h>

const int N = 2e5 + 5;
int a[N], b[N];
int n; int main() {
scanf ("%d", &n);
int tot1 = 0, tot2 = 0;
for (int x, i=0; i<n; ++i) {
scanf ("%d", &x);
if (x != 0) a[tot1++] = x;
}
for (int x, i=0; i<n; ++i) {
scanf ("%d", &x);
if (x != 0) b[tot2++] = x;
}
int s = 0;
for (; s<tot2; ++s) {
if (b[s] == a[0]) break;
}
bool flag = true;
for (int i=0; i<tot1; ++i) {
if (b[(i+s)%(n-1)] != a[i]) {
flag = false; break;
}
}
if (flag) puts ("YES");
else puts ("NO"); return 0;
}

数位DP C - XOR Equation

题意:求多少对(a, b)满足a + b == s && a ^ b == x

分析:s和x拆分成二进制,dp[i][4][0/1]第一维是二进制长度,第二维是a和b二进制下第i位的方案(01,10,00,11),第三维是是否第i位会进位,递推一下能够在log(N)解决.

#include <bits/stdc++.h>

typedef long long ll;
int s[41], x[41];
ll dp[41][4][2]; void updata(int id) {
if (s[id] == x[id]) {
if (s[id] == 0) { //0 0
if (id == 0) {
dp[id][2][0] = dp[id][3][1] = 1;
}
else {
for (int j=0; j<4; ++j) {
dp[id][2][0] += dp[id-1][j][0];
dp[id][3][1] += dp[id-1][j][0];
}
}
}
else { //1 1
if (id == 0) {
dp[id][0][0] = dp[id][1][0] = 1;
}
else {
for (int j=0; j<4; ++j) {
dp[id][0][0] += dp[id-1][j][0];
dp[id][1][0] += dp[id-1][j][0];
}
}
}
}
else {
if (s[id] == 0) { //0 1
for (int j=0; j<4; ++j) {
dp[id][0][1] += dp[id-1][j][1];
dp[id][1][1] += dp[id-1][j][1];
}
}
else { //1 0
for (int j=0; j<4; ++j) {
dp[id][2][0] += dp[id-1][j][1];
dp[id][3][1] += dp[id-1][j][1];
}
}
}
} int main() {
ll S, X; std::cin >> S >> X;
if (X == 0) {
puts ("1"); return 0;
}
bool same = false;
if (S == X) same = true;
int n = 0, m = 0;
while (S) {
if (S & 1) s[n++] = 1;
else s[n++] = 0;
S >>= 1;
}
while (X) {
if (X & 1) x[m++] = 1;
else x[m++] = 0;
X >>= 1;
}
int len = std::max (n, m);
for (int i=0; i<len; ++i) {
updata (i);
}
ll ans = 0;
for (int i=0; i<4; ++i) {
ans += dp[len-1][i][0];
}
if (same && ans >= 2) ans -= 2;
std::cout << ans << '\n'; return 0;
}

树状数组 D - Factory Repairs

单点更新以及区间求和,不过不知道该点是k天前还是后,树状数组开二维.

import java.io.*;
import java.util.*; public class Main {
static int[][] C;
static int[] A;
static int n;
static void updata(int i, int j, int x) {
while (i <= n) {
C[i][j] += x;
i += i & -i;
}
}
static int query(int i, int j) {
int ret = 0;
while (i > 0) {
ret += C[i][j];
i -= i & -i;
}
return ret;
}
static int min(int a, int b) {
if (a < b) return a;
else return b;
}
public static void main(String[] args) {
Scanner cin = new Scanner (new BufferedInputStream (System.in));
n = cin.nextInt ();
int k = cin.nextInt ();
int a = cin.nextInt ();
int b = cin.nextInt ();
int q = cin.nextInt ();
C = new int[n+5][2];
A = new int[n+5];
for (int i=0; i<q; ++i) {
int op = cin.nextInt ();
if (op == 1) {
int d = cin.nextInt ();
int e = cin.nextInt ();
int tmp = A[d]; A[d] += e;
updata (d, 0, min (b, A[d]) - min (b, tmp));
updata (d, 1, min (a, A[d]) - min (a, tmp));
}
else {
int p = cin.nextInt ();
int ans = query (p - 1, 0) + query (n, 1) - query (p+k-1, 1);
System.out.println (ans);
}
}
}
}

贪心+双端队列 E - Package Delivery

题意:m个加油站,每单位油有价格,汽车有油的容量,初始满油,问到终点最少消费多少.

分析: 假设起点也算是一个加油站,免费,且汽车初始空油,那么每一个加油站最多能给汽车充n单位油,那么到下一个加油站要选择之前加油站价格最少的加油,这里用双端队列维护最低价格的加油站.

#include <bits/stdc++.h>

typedef long long ll;
const int N = 2e5 + 5;
int dque[N];
std::pair<int, int> station[N];
int d, n, m; int main() {
scanf ("%d%d%d", &d, &n, &m);
for (int i=0; i<m; ++i) {
scanf ("%d%d", &station[i].first, &station[i].second);
}
station[m++] = std::make_pair (0, 0);
station[m++] = std::make_pair (d, 0);
std::sort (station, station+m);
for (int i=0; i<m-1; ++i) {
if (station[i].first + n < station[i+1].first) {
puts ("-1"); return 0;
}
}
int qs = 0, qe = 0, now = 0; ll ans = 0;
for (int i=0; i<m; ++i) {
int x = station[i].first, p = station[i].second;
while (qs < qe && station[dque[qs]].first + n < x) {
ans += 1ll * (station[dque[qs]].first + n - now) * station[dque[qs]].second;
now = station[dque[qs]].first + n;
qs++;
}
ans += 1ll * (x - now) * station[dque[qs]].second;
now = x;
while (qs < qe && station[dque[qe-1]].second > p) {
qe--;
}
dque[qe++] = i;
}
std::cout << ans << '\n'; return 0;
}

树形DP F - Preorder Test
题意:选择一条k长的DFS先序遍历路径,使得其中的最小值最大
分析:二分枚举最小值,然后枚举每个点出发的大于最小值的路径长度.求路径长度用到树形DP
首先down[u]表示u的子树下最多能走的路径长度,然后第二次dp[u]表示以u为根节点下最多能走的路径长度,up是u以上完整的最大长度

#include <bits/stdc++.h>

const int N = 2e5 + 5;
struct DP {
int sum, mx;
DP operator + (const DP &rhs) const {
return DP {sum + rhs.sum, std::max (mx, rhs.mx)};
}
};
int a[N], total[N], down[N], dp[N];
bool good[N];
std::vector<int> edge[N];
int n, k; void DFS(int u, int fa) {
total[u] = 1;
int sum = 0, mx = 0;
for (auto v: edge[u]) {
if (v == fa) continue;
DFS (v, u);
total[u] += total[v];
if (down[v] == total[v]) {
sum += total[v];
} else {
mx = std::max (mx, down[v]);
}
}
down[u] = sum + mx + 1;
if (!good[u]) down[u] = 0;
} void DFS2(int u, int fa, int up) {    //learn from tourist
std::vector<int> children;
int sum = 0, mx = 0;
for (auto v: edge[u]) {
if (v == fa) continue;
if (down[v] == total[v]) {
sum += down[v];
} else {
mx = std::max (mx, down[v]);
}
children.push_back (v);
}
if (up == n - total[u]) {
sum += up;
} else {
mx = std::max (mx, up);
}
dp[u] = sum + mx + 1;
if (!good[u]) dp[u] = 0; int sz = children.size ();
if (sz == 0) return ;
std::vector<DP> predown (sz + 1);
predown[0] = {0, 0};
for (int i=0; i<sz; ++i) {
int v = children[i];
predown[i+1] = predown[i];
if (down[v] == total[v]) {
predown[i+1].sum += down[v];
} else {
predown[i+1].mx = std::max (predown[i+1].mx, down[v]);
}
}
std::vector<DP> sufdown (sz + 1);
sufdown[sz] = {0, 0};
for (int i=sz-1; i>=0; --i) {
int v = children[i];
sufdown[i] = sufdown[i+1];
if (down[v] == total[v]) {
sufdown[i].sum += down[v];
} else {
sufdown[i].mx = std::max (sufdown[i].mx, down[v]);
}
}
for (int i=0; i<sz; ++i) {
int v = children[i];
DP now = predown[i] + sufdown[i+1];
if (up == n - total[u]) {
now.sum += up;
} else {
now.mx = std::max (now.mx, up);
}
int new_up = now.sum + now.mx + 1;
if (!good[u]) new_up = 0;
DFS2 (v, u, new_up);
}
} bool check(int v) {
for (int i=1; i<=n; ++i) good[i] = (a[i] >= v);
DFS (1, 0);
DFS2 (1, 0, 0);
for (int i=1; i<=n; ++i) {
if (dp[i] >= k) return true;
}
return false;
} int main() {
scanf ("%d%d", &n, &k);
for (int i=1; i<=n; ++i) {
scanf ("%d", a+i);
}
for (int u, v, i=1; i<n; ++i) {
scanf ("%d%d", &u, &v);
edge[u].push_back (v);
edge[v].push_back (u);
}
int low = 0, high = 1000005;
while (low < high) {
int mid = (low + high + 1) >> 1;
if (check (mid)) {
low = mid;
} else {
high = mid - 1;
}
}
printf ("%d\n", low); return 0;
}