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;
}
posted @ 2022-05-21 16:50  Wolfycz  阅读(34)  评论(0编辑  收藏  举报