AtCoder Beginner Contest 266(C- G)「判凸包」「dp」「期望」「基环树」「组合数」

abc好好好。 

C - Convex Quadrilateral (atcoder.jp)

思路:

判凸包,向量叉积gif.latex?%5Cvec%7Ba%7D×gif.latex?%5Cvec%7Bb%7D=|a|*|b|*singif.latex?%5CTheta。叉积<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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值