abc好好好。
C - Convex Quadrilateral (atcoder.jp)
思路:
判凸包,向量叉积
×
=|a|*|b|*sin
。叉积<0即角>180°。
(可以勉勉强强算我写的第一个计算几何哩!
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
struct point {
int x, y;
point(int x = 0, int y = 0): x(x), y(y) {}
} p[10];
point operator - (point a, point b) {
return point(a.x - b.x, a.y - b.y);
}
int cross(point a, point b) { //叉积算法
return a.x * b.y - b.x * a.y;
}
void work() {
for (int i = 1; i <= 4; ++i) {
cin >> p[i].x >> p[i].y;
p[i + 4].x = p[i].x;
p[i + 4].y = p[i].y;
}
for (int i = 1; i <= 4; ++i) {
if (cross(p[i + 1] - p[i], p[i + 2] - p[i + 1]) <= 0) {
cout << "No\n";
return;
}
}
cout << "Yes\n";
}
signed main() {
io;
work();
return 0;
}
D - Snuke Panic (1D) (atcoder.jp)
思路:
dp
把位置和体积塞到桶里(是桶吧?),dp[i][j]表示第i时刻在j个坑里得到的体积数,每一时刻可以由上一时刻的j,j-1,j+1转移,想到了这个后面还蛮好写的。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 1e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int pos[N], a[N];
int dp[N][10];
void work() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int t, x, v;
cin >> t >> x >> v;
pos[t] = x;
a[t] = v;
}
for (int i = 1; i < 5; ++i) {
dp[0][i] = -1e18;
}
for (int i = 1; i < N; ++i) {
for (int j = 0; j < 5; ++j) {
dp[i][j] = dp[i - 1][j];
if (j - 1 >= 0)
dp[i][j] = max(dp[i][j], dp[i - 1][j - 1]);
if (j + 1 < 5)
dp[i][j] = max(dp[i][j], dp[i - 1][j + 1]);
}
dp[i][pos[i]] += a[i];
}
int ans = 0;
for (int i = 0; i < 5; ++i) {
ans = max(ans, dp[N - 1][i]);
}
cout << ans << '\n';
}
signed main() {
io;
work();
return 0;
}
E - Throwing the Die (atcoder.jp)
思路:
和牛子那个游戏的买像像像。
考虑什么情况是最大的期望,如果本轮数比上次的期望小就在上轮停下,如果比上次大就不停。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define i64 __int64
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
double f[N];
void work() {
int n;
cin >> n;
f[1] = 3.5;
for (int i = 2; i <= n; ++i) {
f[i] = 1.0 / 6.0 * max(1.0, f[i - 1]) + 1.0 / 6.0 * max(2.0, f[i - 1]) + 1.0 / 6.0 * max(3.0,
f[i - 1]) + 1.0 / 6.0 * max(4.0, f[i - 1]) + 1.0 / 6.0 * max(5.0, f[i - 1]) + 1.0 / 6.0 * max(6.0, f[i - 1]);
}
printf("%.15lf\n", f[n]);
}
signed main() {
io;
work();
return 0;
}
E-游戏的买_牛客小白月赛67 (nowcoder.com)
思路:
是dp吗??不懂,感觉是逆推。
能够确定的是最后一天的期望一定是1/2a+1/2b。从后往前推,在当天我们是可以知道后面的期望的,如果期望比今天的两个价钱都贵就一定要今天买,如果比今天两个价钱都便宜今天就一定不买,不然就等今天的价钱出来看看如果出的便宜就便宜买,出的贵就后面买。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int a[N],b[N];
double f[N];
void work() {
int n;cin>>n;
for(int i=1;i<=n;++i){
cin>>a[i];
}for(int i=1;i<=n;++i){
cin>>b[i];
}
f[n]=0.5*a[n]+0.5*b[n];
for(int i=n-1;i>0;--i){
if(f[i+1]>max(a[i],b[i]))f[i]=0.5*a[i]+0.5*b[i];
else if(f[i+1]<min(a[i],b[i]))f[i]=f[i+1];
else f[i]=0.5*f[i+1]+0.5*min(a[i],b[i]);
}
printf("%.15lf\n",f[1]);
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
F - Well-defined Path Queries on a Namori (atcoder.jp)
思路:
基环树。(冰茶几好好好我太喜欢了。
先dfs找环,找到环之后冰茶几维护连通块,在同一个连通块里的就可以,不同就不可以。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define i64 __int64
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
vector<int>G[N];
int idx = 0;
set<int>ring;
bool vis[N];
int f[N];
pii d[N];
int dfs(int now, int fa) {
if (vis[now]) {
ring.insert(now);
return now;
} //如果找过他,他必在环里
vis[now] = 1;
for (int i = 0; i < G[now].size(); ++i) {
if (G[now][i] == fa) //如果找到爸爸了就跳过
continue;
int ne = dfs(G[now][i], now); //一直找后面的链
if (ne) { //如果后面的链返回的不是0说明找到环了
ring.insert(now); //把环上的点塞到set里
if (ne == now) //如果递归回到自己头上了说明已经找完环了
return 0;
return ne; //没找完就递归回上一层告诉前面你在环里
}
}
return 0; //如果该点之前都没返回说明该点不在环里,返回0
}
int find(int a) {
if (f[a] != a)
return f[a] = find(f[a]);
return a;
}
void join(int a, int b) {
if (find(a) != find(b))
f[find(a)] = find(b);
}
void work() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int u, v;
cin >> u >> v;
G[u].pb(v);
G[v].pb(u);
d[i] = m_p(u, v);
}
dfs(1, 0);
set<int>::iterator i = ring.begin();
for (int i = 1; i <= n; ++i) {
f[i] = i;
}
for (int i = 1; i <= n; ++i) {
if (ring.count(d[i].fi) && ring.count(d[i].se))
continue;
join(d[i].fi, d[i].se);
}
int q;
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
if (find(u) != find(v))
cout << "No\n";
else
cout << "Yes\n";
}
}
signed main() {
io;
work();
return 0;
}
或者可以对环上每个点都做一次bfs,相当于跑个全图确定根节点,但是好像比并查集慢一些。(数组开小了debug一晚上我是大笨蛋!)
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define i64 __int64
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
vector<int>G[N];
int idx = 0;
set<int>ring;
bool vis[N];
int f[N];
pii d[N];
int dfs(int now, int fa) {
if (vis[now]) {
ring.insert(now);
return now;
}
vis[now] = 1;
for (int i = 0; i < G[now].size(); ++i) {
if (G[now][i] == fa)
continue;
int ne = dfs(G[now][i], now);
if (ne) {
ring.insert(now);
if (ne == now)
return 0;
return ne;
}
}
return 0;
}
void work() {
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
int u, v;
cin >> u >> v;
G[u].pb(v);
G[v].pb(u);
d[i] = m_p(u, v);
}
dfs(1, 0);
set<int>::iterator i = ring.begin();
for (i; i != ring.end(); ++i) {
vector<bool>cha(n + 1); //标记
queue<int>q;
cha[(*i)] = 1;
q.push((*i));
while (!q.empty()) {
int t = q.front();
q.pop();
f[t] = (*i);//连通块标记到环上直接连的点
for (int j = 0; j < G[t].size(); ++j) {
if (!cha[G[t][j]] && !ring.count(G[t][j])) {//又没找过又不在环上
cha[G[t][j]] = 1;
q.push(G[t][j]);
}
}
}
}
int q;
cin >> q;
while (q--) {
int u, v;
cin >> u >> v;
if (f[u] != f[v])
cout << "No\n";
else
cout << "Yes\n";
}
}
signed main() {
io;
work();
return 0;
}
G - Yet Another RGB Sequence (atcoder.jp)
思路:
又被取模坑一把。
捆绑k组RG,把剩下的R放一边,剩下的G,B,K(RG)全排列,是(k+(g-k)+b)!/(k!*(g-k)!*b!) 。(1)
再去放R,注意此时并不是简单插板,而是一个空位可以插多个板,这种类型的算法是在所有可放位置里挑出r-k个位置去放R,这里的不可放位置就是G的左边,所以是C(k+(g-k)+b+(r-k)-(g-k),r-k)。
*(1):设有a个A,b个B,c个C,排列数是(a+b+c)!/(a!*b!*c!)。首先如果abc都不一样那很自然是阶乘,去重是除a!b!c!的原因是a个A在假设的情况里有a!种排列方法,但实际这些排列都一样,所以除掉他。
只要有乘法就取模不然爆ll。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define i64 __int64
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
#define mod 998244353
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
#define inf 0x3f3f3f3f
const int N = 2e6 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
ll fac[N], inv[N];
ll C(int a, int b) {
if (a < b or a < 0 or b < 0)
return 0;
return 1ll * fac[a] % mod * inv[b] % mod * inv[a - b] % mod;
}
void init() {
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for (int i = 2; i < N; ++i) {
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[i] = 1ll * inv[mod % i] * (mod - mod / i) % mod;
}
for (int i = 2; i < N; ++i) {
inv[i] = 1ll * inv[i] * inv[i - 1] % mod;
}
}
void work() {
int r, g, b, k;
cin >> r >> g >> b >> k;
init();
int ans = fac[k + g - k + b] % mod * inv[k] % mod * inv[g - k] % mod * inv[b] % mod; //RG和B和G排列
ans %= mod;
//k+(g-k)+(r-k)+b个数里有(g-k)个位置不能放,其他位置选(r-k)个给R
ans *= C(r + b, r - k);
ans %= mod;
cout << ans << '\n';
}
signed main() {
io;
work();
return 0;
}