AtCoder Beginner Contest 266 Solution
目录更好的阅读体验戳此进入
题面链接
题面 Luogu 链接
abcd 都没什么可说的
[ABC266E] Throwing the Die
题面
你有一个普通均匀的正方体骰子,六个面写有 \(1,2,3,4,5,6\)。你在玩一个游戏,每次丢骰子之后,你可以:
- 如果这是你的第 \(N\) 次投掷,那么你应当结束游戏。
- 否则你可以选择重新投,或者结束游戏。
给定 \(N\),计算如果你希望最后一次投掷时朝上面的期望最大,那么这个期望是多少。
Solution
十分显然且简单的 期望DP。令 $ dp(i) $ 表示最多可以丢 $ i $ 次的答案,显然转移:
\[dp(i) = \sum_{j = 1}^6 \dfrac{dp(i - 1)}{6} \times[dp(i - 1) \ge j] + \dfrac{j}{6} \times [dp(i - 1) \lt j] \]答案即 $ dp(n) $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.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 void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
using namespace std;
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;
ld dp[110];
int main(){
N = read();
for(int i = 1; i <= N; ++i)
for(int j = 1; j <= 6; ++j)
dp[i] += dp[i - 1] > j ? dp[i - 1] / 6.0 : j / 6.0;
printf("%.8Lf\n", dp[N]);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int 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;
}
[ABC266F] Well-defined Path Queries on a Namori
题面
给定一张有 \(N\) 个点、\(N\) 条边的简单连通无向图和 \(Q\) 次询问,对于每次询问,给定 \(x_i,y_i\),表示两点的编号,请你回答第 \(x_i\) 个点和第 \(y_i\) 个点之间是否有且仅有一条简单路径。
- 什么是简单路径?
如果路径上的各顶点均不重复,则称这样的路径为简单路径。
Solution
思路比较容易想到。
根据定义显然为基环树,考虑发现若两点在树上路径经过环那么有多条,反之有且仅有一条。
最开始的思路是断环然后树剖加线段树查有没有环上的点,但是想了一下好像不用这么麻烦。
直接对环标记,对环上每个点进行染色并从非环边搜索下去继续染色,这样如果查询两个点颜色不同的需跨环,反之则不用,跑一下即可。
复杂度应该是 $ O(n + m + q) $ 的。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.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 void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
using namespace std;
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);
struct Edge{
Edge* nxt;
int to;
bool blocked;
OPNEW;
}ed[410000];
ROPNEW;
Edge* head[210000];
int N;
int deg[210000];
bitset < 210000 > inloop;
int col[210000], cnt(0);
basic_string < int > loop;
void dfs(int p, int ccol, int fa = 0){
col[p] = ccol;
for(auto i = head[p]; i; i = i->nxt)
if(!i->blocked && SON != fa)dfs(SON, ccol, p);
}
int main(){
inloop.set();
N = read();
for(int i = 1; i <= N; ++i){
int s = read(), t = read();
head[s] = new Edge{head[s], t, false};
head[t] = new Edge{head[t], s, false};
++deg[s], ++deg[t];
}queue < int > cur;
for(int i = 1; i <= N; ++i)if(deg[i] == 1)cur.push(i), inloop[i] = false;
while(!cur.empty()){
int p = cur.front(); cur.pop();
for(auto i = head[p]; i; i = i->nxt)
if(inloop[SON] && --deg[SON] == 1)
inloop[SON] = false, cur.push(SON);
}
for(int i = 1; i <= N; ++i)if(inloop[i])loop += i;
for(auto p : loop)for(auto i = head[p]; i; i = i->nxt)if(inloop[SON])i->blocked = true;
for(auto p : loop)dfs(p, ++cnt);
int Q = read();
while(Q--){
int s = read(), t = read();
printf("%s\n", !(col[s] ^ col[t]) ? "Yes" : "No");
}
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int 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;
}
[ABC266G] Yet Another RGB Sequence
题面
求符合要求的字符串个数,对 \(998244353\) 取余。
满足要求的字符串 \(s\) 具备以下特性:
-
\(s\) 由
r
、g
、b
构成。 -
\(s\) 中有 \(R\) 个
r
,\(G\) 个g
,\(B\) 个b
,\(k\) 个rg
。
Solution
大概就是我们根据组合意义去考虑,一种思路是先从 $ R $ 和 $ G $ 中各去掉 $ k $ 个,再考虑。
一种思路是考虑先插入 $ g $ 和 $ b $,然后找 $ k $ 个连接成 $ rg $,再将剩余的 $ r $ 插入。两种思路的式子本质相同,最终得到的式子为:
\[{G + B \choose G} \times {G \choose k} \times {R - k + B + k \choose R - k} \]这东西复杂度显然是 $ O(n) $ 的。
然后还有二项式反演的思路,类似于前者去掉 $ k $ 的做法,然后做一遍多重集排列,再用二项式反演将 $ k $ 加上去掉 $ k + 1 $ 加上 $ k + 1 $ 然后 $ \cdots $,发现这玩意就是个二项式,用二项式反演做一下即可,复杂度差不多。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.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 void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
using namespace std;
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 (998244353ll)
template < typename T = int >
inline T read(void);
ll R, G, B, K;
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;
}
ll GetC(ll n, ll m){
if(m > n)return 0;
ll ret(1);
for(int i = 1; i <= n; ++i)(ret *= i) %= MOD;
ll div(1);
for(int i = 1; i <= m; ++i)(div *= i) %= MOD;
for(int i = 1; i <= n - m; ++i)(div *= i) %= MOD;
return ret * qpow(div, MOD - 2) % MOD;
}
int main(){
R = read(), G = read(), B = read(), K = read();
printf("%lld\n", GetC(G + B, G) * GetC(G, K) % MOD * GetC(R + B, R - K) % MOD);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int 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;
}
[ABC266Ex] Snuke Panic (2D)
题面
二维平面直角坐标系中,你初始位于 $ (0, 0) $,每个时刻可以任意向上,左,右走一步,即步进 $ 1 $ 的距离。存在 $ n $ 条收益,当你在 $ T_i $ 时刻走到坐标 $ (X_i, Y_i) $ 的话就能够获得收益 $ A_i $,你需要最大化收益,输出最大值。
Solution
类比 ABC266D 考虑朴素 DP,显然我们可以认为对于无收益的点是无意义的,所以我们可以采用离散化的思想,令 $ dp(i) $ 表示到 $ i $ 条收益的点时的最大收益,转移显然:
\[dp(i) = \max_j\{dp(j)\} + A_i \]这里的 $ j $ 需要满足:
\[t_j \lt t_i \]\[y_j \lt y_i \]\[\lvert x_i - x_j \rvert + y_i - y_j \le t_i - t_j \]显然 $ t_j \lt t_i $ 是无效的限制,对于最后一个因为绝对值的存在可以拆成两个限制,同时关键点在于,若用 CDQ 解决那么我们不能有或起来的条件,故不难发现可以拆成如下两个条件:
\[x_i - x_j + y_i - y_j \le t_i - t_j \]\[x_j - x_i + y_i - y_j \le t_i - t_j \]移项一下:
\[t_j - x_j - y_j \le t_i - x_i - y_i \]\[t_j + x_j - y_j \le t_i + x_i - y_i \]于是就变成了经典的三位偏序问题,我们将 $ y $ 排序解决,然后将最后的对于 $ x, y $ 的限制用树套树解决即可,或用 CDQ 亦可。
这里我们考虑使用 BIT套BIT,即 二维BIT 解决。
注意虽然 BIT 是不支持区间 $ \max $ 的,但支持前缀 $ \max $。
同时注意需要对其全部离散化。
还有一个细节就是在 BIT 里维护的下标是离散化后的值,值则为对应的 $ dp $。
然后还有一个很重要的细节就是对于 $ t_i \lt x_i + y_i $ 的我们应该直接将其丢弃,不能作为转移。
还没完,还有一个细节,就是排序的时候不能只排 $ y $,对于 $ y $ 相同的还需要继续排 $ t - x - y $ 和 $ t + x - y $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW void* Edge::operator new(size_t){static Edge* P = ed; return P++;}
using namespace std;
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;
ll ans(0);
struct Earn{
ll t;
ll x, y;
ll v;
ll ftmp1, ftmp2;
};
basic_string < Earn > E;
basic_string < ll > data1, data2;
class BIT_D1{
private:
unordered_map < int, ll > tr;
public:
int lowbit(int x){return x & -x;}
void Modify(int x, ll v/*ftmp2, dp(j)*/){while(x <= N)tr[x] = max(tr[x], v), x += lowbit(x);}
ll Query(int x){ll ret(0); while(x)ret = max(ret, tr[x]), x-= lowbit(x); return ret;}
};
class BIT_D2{
private:
unordered_map < int, BIT_D1 > tr;
public:
int lowbit(int x){return x & -x;}
void Modify(int x1, int x2, ll v/*ftmp1, ftmp2, dp(j)*/){while(x1 <= N)tr[x1].Modify(x2, v), x1 += lowbit(x1);}
ll Query(int x1, int x2){ll ret(0); while(x1)ret = max(ret, tr[x1].Query(x2)), x1 -= lowbit(x1); return ret;}
}bit;
int main(){
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
N = read();
for(int i = 1; i <= N; ++i){
int t = read(), x = read(), y = read(), v = read();
if(t - x - y < 0)continue;
E += {t, x, y, v, t - x - y, t + x - y};
data1 += t - x - y, data2 += t + x - y;
}
sort(E.begin(), E.end(), [](const Earn &a, const Earn &b)->bool{
if(a.y != b.y)return a.y < b.y;
if(a.ftmp1 != b.ftmp1)return a.ftmp1 < b.ftmp1;
return a.ftmp2 < b.ftmp2;
});
sort(data1.begin(), data1.end()), sort(data2.begin(), data2.end());
for(auto &e : E)
e.ftmp1 = distance(data1.begin(), lower_bound(data1.begin(), data1.end(), e.t - e.x - e.y)) + 1,
e.ftmp2 = distance(data2.begin(), lower_bound(data2.begin(), data2.end(), e.t + e.x - e.y)) + 1;
for(auto e : E){
ll ret = bit.Query(e.ftmp1, e.ftmp2) + e.v;
ans = max(ans, ret);
bit.Modify(e.ftmp1, e.ftmp2, ret);
}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);
int 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-2023_01_12 初稿
标签:AtCoder,return,int,题解,void,ret,266,ll,define From: https://www.cnblogs.com/tsawke/p/17124332.html