AtCoder Beginner Contest 246 题解

AtCoder Beginner Contest 246 Solution

更好的阅读体验戳此进入

题面链接

题面 Luogu 链接

A - Four Points

题面

给定矩形三个点坐标,求另一个点的坐标。

Solution

显然四个点横纵坐标分别两两相等,三个横坐标异或,三个纵坐标异或即可。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

int main(){
    int x1 = read(), y1 = read(), x2 = read(), y2 = read(), x3 = read(), y3 = read();
    printf("%d %d\n", x1 ^ x2 ^ x3, y1 ^ y2 ^ y3);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

B - Get Closer

题面

给定一个向量的坐标表示,求同向模长为 $ 1 $ 的向量坐标。

Solution

语法题没营养,为了凑齐一套题去写的。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

int main(){
    int x = read(), y = read();
    ld base = sqrtl((ld)x * x + (ld)y * y);
    printf("%.8Lf %.8Lf\n", (ld)x / base, (ld)y / base);

    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

C - Coupon

题面

$ n $ 个物品第 $ i $ 个价格为 $ a_i $,有 $ k $ 张 $ x $ 元优惠券,可以叠加,但不能分裂开使用,求全部购买的最少花费。

Solution

对于所有 $ a_i \ge x $ 一直使用优惠券直到 $ a_i \lt x $,然后降序排序用剩余的 $ k $ 个券把前 $ k $ 个抵消,后面的求和即为答案。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

int a[210000];

int main(){
    int N = read(), K = read(), X = read();
    for(int i = 1; i <= N; ++i){a[i] = read();while(K && a[i] >= X)--K, a[i] -= X;}
    sort(a + 1, a + N + 1, greater < int >());
    ll ans(0);
    for(int i = K + 1; i <= N; ++i)ans += a[i];
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

D - 2-variable Function

题面

存在函数 $ f(a, b) = a^3 + a^2b + ab^2 + b^3 $,要求 $ a, b \ge 0 $,给定 $ n $,求最小的 $ f(a, b) $ 满足 $ f(a, b) \ge n \(。\) n \le 10^{18} $。

Solution

显然 $ a, b $ 的范围不超过 $ 10^6 $,则可以枚举 $ a $,二分 $ b $,取最小值即可。

对于二分 $ b $ 的单调性证明,可以考虑固定 $ a $ 为参数,则有函数 $ f(b) = b^3 + ab^2 + a^2b + a^3 $,三次函数不好操作,所以考虑求导,显然 $ f'(b) = 3b^2 + 2ab + a^2 $,因为 $ a, b $ 非负,所以显然导函数 $ f'(b) \ge 0 $,所以范围内函数取值单调递增,于是可以二分。

或者不进行二分,根据单调性,显然 $ a $ 减小时 $ b $ 不会比上次更大,所以写个双指针枚举,$ a $ 升序,$ b $ 降序,小于 $ n $ 时直接 break 即可。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

ll Func(int a, int b){
    return (ll)a * a * a + (ll)a * a * b + (ll) a * b * b + (ll)b * b * b;
}

int main(){
    ll N = read<ll>();
    ll mn(LLONG_MAX);
    for(int x = 0; x <= (int)1e6; ++x){
        int l = 0, r = (int)1e6;
        ll cur(-1);
        while(l <= r){
            int mid = (l + r) >> 1;
            ll tmp = Func(x, mid);
            if(tmp >= N)cur = tmp, r = mid - 1;
            else l = mid + 1;
        }
        mn = min(mn, cur);
    }printf("%lld\n", mn);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

E - Bishop 2

题面

给定有障碍的网格图,. 为空地,# 为障碍。给定起点终点,每次移动仅可以斜向走任意长度,问从起点到终点的最少移动次数,可能无解,无解输出 -1

Solution

BFS 较为显然,因为时限 6000ms,只要写的不太丑直接搜也能过。对于本题,使用 01BFS 较为显然。我们在宽搜每次搜一步且距离仅为 $ 1 $,并记录上一步的方向,如果同向则认为走了一个 $ 0 $ 边,异向则为 $ 1 $ 边,开个双端队列,同向插到前面,反之插到后面,按照正常宽搜每次取队头扩展即可。

需要注意对于 01BFS 时,我们判断是否走过和是否为答案的时候,需要在从队头取元素的时候判断,而不是在扩展的时候判断。因为如果在某一次由 $ 1 $ 扩展的时候如果直接把 $ vis $ 设为 $ \texttt{true} $,但是这可能会导致后面从 $ 0 $ 扩展的,本应能插在队列中比这个更前面的更优的无法转移,使答案更劣。同时我们也需要考虑到不同方向的时候扩展也是不同,所以 $ vis $ 中也要考虑到方向这一维。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define CHK(x, y) (x >= 1 && x <= N && y >= 1 && y <= N && !mp[x][y])

template<typename T = int>
inline T read(void);

int N;
int dx[10] = {0,  -1, -1, 1, 1};
int dy[10] = {0,  -1, 1, -1, 1};
int vis[1600][1600][5];
bool mp[1600][1600];

struct Status{
    int x, y;
    int dir;//direction 1, 2, 3, 4
    int dist;
}S, T;
void Init(void){
    char c = getchar();
    for(int i = 1; i <= N; ++i)for(int j = 1; j <= N; ++j){
        while(c != '.' && c != '#')c = getchar();
        mp[i][j] = c == '.' ? false : true;
        c = getchar();
    }
}
void bfs(void){
    deque < Status > dq;
    dq.push_back(S);
    while(!dq.empty()){
        auto tp = dq.front(); dq.pop_front();
        if(vis[tp.x][tp.y][tp.dir])continue;
        vis[tp.x][tp.y][tp.dir] = true;
        if(tp.x == T.x && tp.y == T.y)
            printf("%d\n", tp.dist), exit(0);
        // printf("Current pos (%d, %d): dis = %d, dir = %d\n", tp.x, tp.y, tp.dist, tp.dir);
        for(int i = 1; i <= 4; ++i){
            int tx = tp.x + dx[i], ty = tp.y + dy[i];
            if(!CHK(tx, ty))continue;
            if(i == tp.dir)dq.push_front(Status{tx, ty, i, tp.dist});
            else dq.push_back(Status{tx, ty, i, tp.dist + 1});
        }
    }printf("-1\n");
}

int main(){
    // freopen("test_11.txt", "r", stdin);
    N = read();
    int x = read(), y = read(); S = Status{x, y, 0, 0};
    x = read(), y = read(); T = Status{x, y, 0, 0};
    Init();
    bfs();
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

F - typewriter

题面

给定 $ n $ 个字符串,字符集为小写字母,可以任意选择一个字符串,作为字符库,然后(可多次选择同一字符)任意组成长度为 $ l $ 的字符串,求一共能形成多少种长度为 $ l $ 的字符串。

Solution

容斥较为显然,显然最终答案也就是,用任意一个字符集形成的字符串减去任意两个的加上任意三个...于是我们考虑令全集为 $ S = 2^n - 1 $ 然后对其进行枚举子集,二进制第 $ i $ 位为 $ 1 $ 或 $ 0 $ 代表是否考虑这个数,所以枚举的时候直接 __builtin_popcount() 算一下个数,奇数代表加,反之减。然后对于每一个字符串,因为字符集较小,也用一个 int 的二进制表示是否存在对应的字符,然后把需要的字符串都与起来,设数量为 $ \xi $,则此次运算的字符集大小则为 $ \xi $,所以此次答案为 $ \xi^l $,加减考虑好即可。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MOD 998244353

template<typename T = int>
inline T read(void);

int N, L;
int str[20];
int readStr(void){
    int ret(0);
    char c = getchar();
    while(!islower(c))c = getchar();
    vector < int > val;
    while(islower(c)){
        ret |= 1 << (c - 'a');
        c = getchar();
    }return ret;
}
ll qpow(ll a, ll b){
    ll ret(1), mul(a);
    while(b){
        if(b & 1)ret = ret * mul % MOD;
        b >>= 1;
        mul = mul * mul % MOD;
    }return ret;
}

int main(){
    N = read(), L = read();
    ll ans(0);
    for(int i = 1; i <= N; ++i)str[i] = readStr();
    // for(int i = 1; i <= N; ++i)
    //     cout << bitset < 32 > (str[i]) << endl;
    int Smx = (1 << N) - 1;
    // cout << "Smx" << bitset < 32 > (Smx) << endl;
    for(int S = Smx; S; S = (S - 1) & Smx){
        // cout << "S:" << bitset < 32 > (S) << endl;
        int cnt = __builtin_popcount(S);
        int tot((1 << 26) - 1);
        // cout << "tot_pre:" << bitset < 32 > (S) << endl;
        for(int i = 0; i <= N - 1; ++i)
            if((1 << i) & S)tot &= str[i + 1];
        // cout << "tot:" << bitset < 32 > (tot) << endl;
        ans = (ans + qpow(__builtin_popcount(tot), L) * ((cnt & 1) ? 1 : -1) + MOD) % MOD;
    }
    printf("%lld\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

G - Game on Tree 3

题面

类似于 LG-P3554 [POI2013]LUK-Triumphal arch,给定一棵树,有点权,B 初始在 $ 1 $,每轮 A 选择一个点将权值变为 $ 0 $,然后 B 移动一次,B 可在任意时刻停止游戏然后获得所在点上的权值的得分,两人均采取最优策略那么最终 B 最少会拿到多少的得分。

Solution

与 POI2013 基本相同,对于本题依然考虑二分答案,对于当前的答案 $ k $,认为大于 $ k $ 的贡献为 $ 1 $,小于等于的为 $ 0 $,于是显然有 dp,令 $ dp(i) $ 表示在 $ i $ 节点上时需要额外多少次的变为 $ 0 $ 的操作,显然有转移 $ dp(i) = \max(\sum_{j \in son(i)} dp(j) - 1, 0) + \left[ v(i) \gt k \right] $,最后判断一下根节点 $ 1 $ 为 $ 0 $ 则合法,反之不合法。

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

template<typename T = int>
inline T read(void);

int N;
struct Edge{
    Edge* nxt;
    int to;
    OPNEW;
}ed[410000];
ROPNEW(ed);
Edge* head[210000];

int val[210000];
int mnval(INT_MAX), mxval(-1);
int f[210000];

void dfs(int k, int p = 1, int fa = 0){
    for(auto i = head[p]; i; i = i->nxt){
        if(SON == fa)continue;
        dfs(k, SON, p);
        f[p] += f[SON];
    }
    f[p] -= 1;
    f[p] = max(0, f[p]);
    f[p] += val[p] > k ? 1 : 0;
}
bool Check(int K){
    memset(f, 0, sizeof(f));
    dfs(K);
    return f[1] == 0;
}

int main(){
    N = read();
    for(int i = 2; i <= N; ++i)val[i] = read(), mxval = max(mxval, val[i]);
    for(int i = 1; i <= N - 1; ++i){
        int s = read(), t = read();
        head[s] = new Edge{head[s], t};
        head[t] = new Edge{head[t], s};
        if(s == 1)mnval = min(mnval, val[t]);
        if(t == 1)mnval = min(mnval, val[s]);
    }if(!head[1]->nxt)mnval = 0;
    int l = mnval, r = mxval;
    int ans(-1);
    while(l <= r){
        int mid = (l + r) >> 1;
        Check(mid) ? ans = mid, r = mid - 1 : l = mid + 1;
    }printf("%d\n", ans);
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template<typename T>
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

Ex - 01? Queries

题面

给定长度为 $ N $ 的仅包含 01? 的字符串 $ S $,给定 $ Q $ 组询问 $ (x_1, c_1), (x_2, c_2), \cdots, (x_q, c_q) $,每次将原字符串中 $ x_i $ 位置的字符改为 $ c_i $,然后输出 $ S $ 有多少种非空子串,? 需任意替换为 01

Solution

太长了这里就直接放个链接吧。。

ABC246Ex 01? Queries Solution

Code

#define _USE_MATH_DEFINES
#include <bits/extc++.h>

#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}

using namespace std;
using namespace __gnu_pbds;

mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}

typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;

#define MAXN (int)(1e5 + 100)
#define MOD (int)(998244353)

template< typename T = int >
inline T read(void);

int N, Q;
int S[MAXN];

struct Matrix3{
    int val[3][3];
    Matrix3(int v00, int v01, int v02, int v10, int v11, int v12, int v20, int v21, int v22):
        val{
            {v00, v01, v02},
            {v10, v11, v12},
            {v20, v21, v22}
        }{;}
    Matrix3(int S):
        val{
            {1, S != 0, 0},
            {S != 1, 1, 0},
            {S != 1, S != 0, 1}
        }{;}
    Matrix3(int val[][3]){for(int i = 0; i <= 2; ++i)for(int j = 0; j <= 2; ++j)this->val[i][j] = val[i][j];}
    Matrix3(void) = default;
    friend const Matrix3 operator * (const Matrix3 &x, const Matrix3 &y){
        int val[3][3]; memset(val, 0, sizeof val);
        for(int i = 0; i <= 2; ++i)for(int j = 0; j <= 2; ++j)for(int p = 0; p <= 2; ++p)
            val[i][j] = ((ll)val[i][j] + (ll)x.val[i][p] * y.val[p][j] % MOD) % MOD;
        return Matrix3(val);
    }
    void Print(void){
        for(int i = 0; i <= 2; ++i)for(int j = 0; j <= 2; ++j)
            printf("%d%c", val[i][j], j == 2 ? '\n' : ' ');
    }
}mt[MAXN];

class SegTree{
private:
    Matrix3 tr[MAXN << 2];
    #define LS (p << 1)
    #define RS (LS | 1)
    #define MID ((gl + gr) >> 1)
public:
    void Pushup(int p){tr[p] = tr[LS] * tr[RS];}
    void Build(int p = 1, int gl = 1, int gr = N){
        if(gl == gr)return tr[p] = mt[gl = gr], void();
        Build(LS, gl, MID);
        Build(RS, MID + 1, gr);
        Pushup(p);
    }
    void Modify(int idx, Matrix3 v, int p = 1, int gl = 1, int gr = N){
        if(gl == gr)return tr[p] = v, void();
        if(idx <= MID)Modify(idx, v, LS, gl, MID);
        else Modify(idx, v, RS, MID + 1, gr);
        Pushup(p);
    }
    Matrix3 Query(void){return tr[1];}
}st;

int main(){
    N = read(), Q = read();
    string s; cin >> s;
    for(int i = 1; i <= (int)s.size(); ++i)
        S[i] = s.at(i - 1) == '?' ? -1 : int(s.at(i - 1) - '0'),
        mt[i] = Matrix3(S[i]);
    st.Build();
    Matrix3 origin(0, 0, 1, 0, 0, 0, 0, 0, 0);
    while(Q--){
        int p = read();
        char c = getchar(); while(c != '0' && c != '1' && c != '?')c = getchar();
        int flag = c == '?' ? -1 : int(c - '0');
        st.Modify(p, Matrix3(flag));
        auto ans = origin * st.Query();
        printf("%d\n", (int)((ll)(ans.val[0][0] + ans.val[0][1]) % MOD));
    }
    fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
    return 0;
}

template < typename T >
inline T read(void){
    T ret(0);
    short flag(1);
    char c = getchar();
    while(c != '-' && !isdigit(c))c = getchar();
    if(c == '-')flag = -1, c = getchar();
    while(isdigit(c)){
        ret *= 10;
        ret += int(c - '0');
        c = getchar();
    }
    ret *= flag;
    return ret;
}

UPD

update-2022_10_24 初稿

posted @ 2022-10-24 08:23  Tsawke  阅读(38)  评论(0)    收藏  举报