Codeforces Round #783 (Div. 2)
A:从(1,1)移动至(n,m),每次可以往上下左右一个方向移动一次,不能连续两次往同一个方向移动,问是否可行及最少移动次数(solution略)
B:给定n个人和m张椅子(组成环形),每个人要求左右都至少有\(A_i\)的空位不坐人,问是否能将n个人安顿在这m个座位上。
将\(A\)升序排序,贪心构造易得其需要的最少座位数为\(n+A_n+\sum\limits_{i=2}^nA_i\)
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Fi first
#define Se second
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e5;
int A[N + 10];
int main() {
int T = read(0);
while (T--) {
int n = read(0), m = read(0);
for (int i = 1; i <= n; i++) A[i] = read(0);
sort(A + 1, A + 1 + n);
ll res = A[n] + n;
for (int i = 2; i <= n; i++) res += A[i];
printf(res <= m ? "YES\n" : "NO\n");
}
return 0;
}
C:给定序列\(A\),和空序列\(B\),每次操作可以让\(B_i+=A_i\),或者\(B_i-=A_i\),问最少需要几次操作可以让\(B\)严格升序?
由于\(n\)较小,故我们可以考虑\(n^2\)做法,暴力枚举一个不动点(即\(B_i=0\)),再从\(i\)位置开始依次确定每个点保证严格升序所需的最小操作次数即可
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Fi first
#define Se second
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 5e3;
int A[N + 10], n;
ll solve(int x) {
ll res = 0, Now = 0;
for (int j = x + 1; j <= n; j++) {
res += Now / A[j] + 1;
Now = 1ll * (Now / A[j] + 1) * A[j];
}
Now = 0;
for (int j = x - 1; j; j--) {
res += Now / A[j] + 1;
Now = 1ll * (Now / A[j] + 1) * A[j];
}
return res;
}
int main() {
n = read(0); ll Ans = lMax;
for (int i = 1; i <= n; i++) A[i] = read(0);
for (int i = 1; i <= n; i++) Ans = min(Ans, solve(i));
printf("%lld\n", Ans);
return 0;
}
D:给定序列\(A\),定义一段区间\([l,r]\)的分值为\(\left\{\begin{aligned}(r-l+1)&,s>0\\0&,s=0\\-(r-l+1)&,s<0\end{aligned}\right.\),其中\(s=\sum\limits_{i=l}^ra_i\),求划分成若干个子段后的最大分值和
我们记\(F[i]\)表示\(1\sim i\)的最大分值,考虑转移:\(F[r]=F[l-1]+(r-(l-1))\),如果\(\sum\limits_{i=l}^ra_i>0\)。如何满足条件?我们将前缀和离散化,用树状数组维护即可。树状数组内存\(F[i]-i\),则每次更新即为\(F[x]=\mathrm{Query}()+x\)。具体见代码
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define Fi first
#define Se second
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 5e5;
int A[N + 10], F[N + 10], n;
ll Pre[N + 10], list[N + 10];
struct S1 {
int Tree[N + 10];
S1() {
memset(Tree, 128, sizeof(Tree));
}
void insert(int x, int v) { for (; x <= n; x += lowbit(x)) Tree[x] = max(Tree[x], v); }
int Query(int x) {
int res = -iMax;
for (; x; x -= lowbit(x)) res = max(res, Tree[x]);
return res;
}
}TA;//Tree Array
void clear() {
for (int i = 1; i <= n; i++)
F[i] = TA.Tree[i] = -iMax;
}
int main() {
int T = read(0);
memset(F, 128, sizeof(F));
while (T--) {
n = read(0);
for (int i = 1; i <= n; i++) {
Pre[i] = Pre[i - 1] + (A[i] = read(0));
list[i] = Pre[i];
}
list[0] = Pre[0];
sort(list, list + 1 + n);
int m = unique(list, list + 1 + n) - list;
for (int i = 0; i <= n; i++) Pre[i] = lower_bound(list, list + m, Pre[i]) - list + 1;
TA.insert(Pre[0], F[0] = 0);
for (int i = 1; i <= n; i++) {
F[i] = max(F[i - 1] + (A[i] < 0 ? -1 : 0), TA.Query(Pre[i] - 1) + i);
TA.insert(Pre[i], F[i] - i);
}
printf("%d\n", F[n]);
clear();
}
return 0;
}
E:在一个\(n\times m\)的棋盘上放置半皇后,半皇后可以攻击同行同列以及同一主对角线上的所有格子。问最少放置多少个半皇后,可以使所有的格子都至少被一个半皇后攻击到?
构造题。因为半皇后只能攻击主对角线,因此我们考虑将多个半皇后沿反对角线放置。多次试验后可得,将半皇后分为两组,沿反对角线放置在左上和右下角是最优做法。
具体而言,当\(n=3k-1\)时,我们在左上角放置\(k\)个半皇后,在右下角放置\(k-1\)个半皇后即可。对于其他两种情况,我们只随便找个角落,放置1~2个半皇后即可
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
struct node {
int x, y;
node(int _x = 0, int _y = 0) { x = _x, y = _y; }
void print() {
printf("%d %d\n", x, y);
}
};
vector<node>Ans;
int main() {
int n = read(0);
if (n % 3 == 1) {
Ans.push_back(node(n, n));
n--;
}
if (n) {
if (n % 3 == 0) {
Ans.push_back(node(n, n));
n--;
}
int m = (n + 1) / 3;
for (int i = 1; i <= m; i++)
Ans.push_back(node(i, m - i + 1));
for (int i = 1; i < m; i++)
Ans.push_back(node(n - i + 1, n - m + 1 + i));
}
printf("%d\n", (int)Ans.size());
for (auto p : Ans)
p.print();
return 0;
}
F:给定一棵树,若一条边的两端点入度和为偶数,则可将其删去。问是否存在一种删边的顺序,使得所有边都可以被删掉?
对于一个点而言,若其度数为奇,则必定先删除与奇度数点相连的边,然后再为与偶度数点相连的边,依此类推,可知其删边必定是交替存在的。故一个点的所有出边内,要么偶度数与奇度数点相同,要么奇度数点比偶度数点多一。
此外,考虑可以删边的情况,一条边的两个端点度数必定全为奇或者偶,我们记前者为奇边,后者为偶边。我们可以从叶子节点开始,依此确定每条边在删除的时候两端点的奇偶性,并且判断其是否合法。
如果合法,则我们可以从根节点开始递归处理出一条可行的方案,具体可以见代码
/*program from Wolfycz*/
#include<map>
#include<set>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lMax 1e18
#define MK make_pair
#define iMax 0x7f7f7f7f
#define sqr(x) ((x)*(x))
#define pii pair<int,int>
#define lowbit(x) ((x)&(-x))
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
template<typename T>inline T read(T x) {
int f = 1; char ch = getchar();
for (; ch < '0' || ch>'9'; ch = getchar()) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
return x * f;
}
inline void print(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 2e5;
int pre[(N << 1) + 10], now[N + 10], child[(N << 1) + 10], tot;
void link(int x, int y) { pre[++tot] = now[x], now[x] = tot, child[tot] = y; }
void connect(int x, int y) { link(x, y), link(y, x); }
int Parity[N + 10];
bool Flag;
void Dfs(int x, int fa) {
int Cnt[2] = { 0, 0 };
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
if (son == fa) continue;
Dfs(son, x);
Cnt[Parity[son]]++;
}
if (x != 1) {
Parity[x] = (Cnt[0] >= Cnt[1]);
Cnt[Parity[x]]++;
}
Flag |= Cnt[1]<Cnt[0] || Cnt[1]>Cnt[0] + 1;
}
void solve(int x, int fa) {
vector<int>Ans[2];
int Cnt = 0;
for (int p = now[x]; p; p = pre[p]) {
int son = child[p];
Ans[Parity[son == fa ? x : son]].push_back(son == fa ? x : son);
Cnt++;
}
int ID = Cnt % 2;
for (int i = 0; i < Cnt; i++) {
int V = Ans[ID].back();
if (V == x) {
printf("%d %d\n", x, fa);
}
else solve(V, x);
Ans[ID].pop_back();
ID ^= 1;
}
}
void clear(int n) {
tot = Flag = 0;
for (int i = 1; i <= n; i++) now[i] = Parity[i] = 0;
}
int main() {
int T = read(0);
while (T--) {
int n = read(0);
for (int i = 1; i < n; i++) {
int x = read(0), y = read(0);
connect(x, y);
}
Dfs(1, 0);
if (Flag) {
printf("NO\n");
goto CLEAR;
}
printf("YES\n");
solve(1, 0);
CLEAR:
clear(n);
}
return 0;
}