AtCoder Beginner Contest 361(A-G)

场次:AtCoder Beginner Contest 361(A-G)

A.

tag:模拟
思路:

模拟即可

代码:

B.

tag:几何
思路:

可以先考虑一维,比如先考虑 z 轴是否相交,如果两个长方体的 z 轴不相交,那么不可能会有交的体积。如果相交,那么接下来就可以降维,投影到平面上继续相同的考虑。如此进行下去即可。

代码:

C.

tag:贪心
思路:

可以推知,先排序,每次操作肯定是去掉最大或者最小的一个数,最后形成的是左边去掉一段连续的数,右边去掉一段连续的数。

由此可以进行枚举,最后左边删了i个数,那么右边删了k-i个数。计算最小答案即可。

代码:
#include<bits/stdc++.h>
#define L(i, l, r) for(int i = l; i <= r; i ++)
#define R(i, l, r) for(int i = r; i >= l; i --)
#define ll long long
#define fi first
#define se second
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
#define ull unsigned long long

using namespace std;
const ll N = 1e6 + 10, mod = 998244353;

void add(ll &x, ll y){
	x += y;
	while(x > mod) x -= mod;
	while(x < 0) x += mod;
}
ll a[N];
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// int T; cin >> T; while(T --) solve();
	int n, k;  cin >> n >> k;
	L(i, 1, n) cin >> a[i];
	sort(a + 1, a + 1 + n);

	ll ans = 1e18;

	for(int i = 0; i <= k; i ++){
		int j = k - i;
		ll  l = a[i + 1], r = a[n - j];
		ans = min(ans, r - l);
	}
	cout << ans << endl;

	return 0;
}

D.

tag:状态压缩dp
思路:

n <= 14, 小数据范围很可能会是状压dp。

考虑状态压缩,用0表示B, 用1表示W, 最后可以得到一个小于 (1ll << 16) 的数来表示当前的棋子状态。

d p [ i ] [ j ] dp[i][j] dp[i][j]​, i 表示当前"…“在哪个位置,j表示棋子状态,”…"也用0来表示,方便计算转移状态。

代码:
#include<bits/stdc++.h>
#define L(i, l, r) for(int i = l; i <= r; i ++)
#define R(i, l, r) for(int i = r; i >= l; i --)
#define ll long long
#define fi first
#define se second
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
#define ull unsigned long long

using namespace std;
const ll N = 1e6 + 10, mod = 998244353;
const ll M = 1ll << 16 - 1;

void add(ll &x, ll y){
	x += y;
	while(x > mod) x -= mod;
	while(x < 0) x += mod;
}
ll qsm(ll a, ll b){
    ll res = 1;
	while(b){
		if(b & 1) res = res * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return res;
}
ll dp[16][M];

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// int T; cin >> T; while(T --) solve();
	int n; cin >> n;
	string S, T; cin >> S >> T;
	ll sw = 0, tw = 0;

	L(i, 0, n - 1){
		if(S[i] == 'W') sw ++; // 1
		if(T[i] == 'W') tw ++;
	}
	if(sw != tw){ cout << -1 << endl; return 0;}
	ll st = 0, ed = 0;
	for(int i = 0; i < n; i ++){
		if(S[i] == 'W') st += 1ll << i;
		if(T[i] == 'W') ed += 1ll << i;
	}
	me(dp, -1);
	dp[n][st] = 0;
	queue<pair<int, int>> q;
	q.push({n, st});

	//cout << st << " " << ed << endl;

	while(!q.empty()){
		auto [y, v] = q.front(); q.pop();

		for(int x = 0; x <= n; x ++){
			if(x == y || x + 1 == y || x - 1 == y) continue;
			ll c = (v >> x) % 4;
			ll nxt = v - (c << x) + (c << y);

			if(dp[x][nxt] > dp[y][v] + 1 || dp[x][nxt] == -1){
				dp[x][nxt] = dp[y][v] + 1;
				q.push({x, nxt});
			}
		} 
	}
	cout << dp[n][ed] << endl;

	return 0;
}

E.

tag:树形dp, 换根dp
思路:

题意:给定一棵树,每条边都有边权,要求走完所有点的最短距离。

题目给定一颗无根树,先考虑固定一个根起点的情况,由于要走完所有的点,所以如果不在我们的最后一条路径上的话,其余每条路径都要经过两遍,由此对于当前情况的最好结果是 : 路径和 * 2 - 最长子路径。

然后考虑随便挑一个点的话,就是最长子路径转化成树的一条最长路径,求树的直径即可。

利用树形dp求解。

son1[]: 子树最长路径的儿子节点, son2[]: 子树次长路径的儿子节点。

mx1[]:子树最长路径,mx2[]:子树次长路径。

换根时,考虑是子树的两条路径,还是子树一条路径与父节点的路径和哪个最大就可。

代码:
#include<bits/stdc++.h>
#define L(i, l, r) for(int i = l; i <= r; i ++)
#define R(i, l, r) for(int i = r; i >= l; i --)
#define ll long long
#define fi first
#define se second
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
#define ull unsigned long long

using namespace std;
const ll N = 1e6 + 10, mod = 998244353;

void add(ll &x, ll y){
	x += y;
	while(x > mod) x -= mod;
	while(x < 0) x += mod;
}
ll ans;
ll son1[N], son2[N], sum[N];
ll  mx1[N],  mx2[N];
vector<pair<int, ll>> g[N];

void dfs(int u, int fa){
	for(auto [v, w]: g[u]){
		if(v == fa) continue;
		dfs(v, u);
		if(mx1[v] + w > mx1[u]){
			mx1[u]  = mx1[v] + w;
			son1[u] = v;
		}else if(mx1[v] + w > mx2[u]){
			mx2[u]  = mx1[v] + w;
			son2[u] = v;
		}
	}
}
void dfs_2(int u, int fa){
	for(auto [v, w] : g[u]){
		if(v == fa) continue;
		sum[v] = mx1[v] + mx2[v];
		if(son1[u] != v){
			sum[v] = max(sum[v], mx1[v] + w + mx1[u]);
		}else{
			sum[v] = max(sum[v], mx1[v] + w + mx2[u]);
		}
		dfs_2(v, u);
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// int T; cin >> T; while(T --) solve();
	int n; cin >> n;
	L(i, 1, n - 1){
		int a, b, c; cin >> a >> b >> c;
		g[a].push_back({b, c});
		g[b].push_back({a, c});
		ans += 2 * c;
	}
	dfs(1, 0);

	sum[1] = mx1[1] + mx2[1];
	dfs_2(1, 0);
	ll mx = *max_element(sum + 1, sum + 1 + n);
	cout << ans - mx << endl;

	return 0;
}

F.

tag:数学,暴力
思路:

首先根据范围 1 0 18 10^{18} 1018考虑时间复杂度,容易计算 x = a * a的贡献,一共为 sqrt(n); 然后考虑三次方以上的数的贡献,此时就可以枚举了,从1到 1 0 6 10^6 106枚举,暴力计算,排除重复计算结果即可。

注意某些情况,sqrt(n)不是平方数个数,要用sqrtl才行。

代码:
#include<bits/stdc++.h>
#define L(i, l, r) for(int i = l; i <= r; i ++)
#define R(i, l, r) for(int i = r; i >= l; i --)
#define ll long long
#define fi first
#define se second
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
#define ull unsigned long long

using namespace std;
const ll N = 1e6 + 10, mod = 998244353;

void add(ll &x, ll y){
	x += y;
	while(x > mod) x -= mod;
	while(x < 0) x += mod;
}

ll ans = 0;
ll n;
int vis[N];

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// int T; cin >> T; while(T --) solve();
	cin >> n;
	ans = sqrtl(n); 
	// 注意某些case 上 平方数 != sqrt(n)
	// 用 sqrt 会 错一个点 sqrt -> double
	// sqrtl -> long double

	map<ll, int> mp;
	
	for(ll i = 2; i * i * i <= n; i ++){
		ll t = sqrt(i);
		if(t * t == i) continue;
		if(vis[i]) continue;
		
		ll j = i * i;
		while(j <= n / i){
			j = j * i;
			ll x = sqrt(j);
			if(x * x == j) continue;

			if(j < N){
				if(!vis[j]){
					ans ++, vis[j] = 1;
				}
			}else{
				if(!mp[j] && j <= n){
					ans ++, mp[j] = 1;
				}
			}
		}
	}
	cout << ans << endl;

	return 0;
}

G.

tag:BFS,模拟
思路:

题意:在平面上格子点中放置n个石头,找到被石头包围的格子点的数量。

如图,按照题解的写法,将空白格子划分为 1 * x 的矩形,然后将包含(-1,-1)的矩形放入进行bfs即可。
在这里插入图片描述
每行最多扫两边。总时间复杂度大概为O(n + m).

代码实现有点烂,最好看看别人的。

代码:
#include<bits/stdc++.h>
#define L(i, l, r) for(int i = l; i <= r; i ++)
#define R(i, l, r) for(int i = r; i >= l; i --)
#define ll long long
#define fi first
#define se second
#define vi vector <int>
#define sz(a) ((int) (a).size())
#define me(f, x) memset(f, x, sizeof(f))
#define ull unsigned long long

using namespace std;
const ll N = 2e5 + 10, mod = 998244353;

void add(ll &x, ll y){
	x += y;
	while(x > mod) x -= mod;
	while(x < 0) x += mod;
}

struct Lines{
	int y, l, r;
}L[2 * N];
int M = N - 5; 
int vis[2 * N];
vector<int> Y[N];
vector<int> G[N];
vector<int> near[2 * N][2]; // 0:up 1:down

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	// int T; cin >> T; while(T --) solve();
	int n; cin >> n;
	for(int i = 1; i <= n; i ++){
		int x, y; cin >> x >> y;
		Y[y].push_back(x);
	}
	for(int i = M; i >= 0; i --){
		sort(Y[i].begin(), Y[i].end());
	}
	int tot = 0, d = 1, c = 0;
	L[++ tot] = {-1, -1, M + 1};
	G[-1 + d].push_back(tot);
	
	for(int i = 0; i <= M; i ++){
		int lst = -1;
		for(auto &t : Y[i]){
			if(lst <= t - 1){
				 L[++ tot] = {i, lst, t - 1};
				 G[i + d].push_back(tot);
			}
			lst = t + 1;
		}
		L[++ tot] = {i, lst, M + 1};
		G[i + d].push_back(tot);

		// if(i <= 5){
		// 	cout << i << ": " << endl;
		// }
		// for(auto t : G[i + d]){
		// 	if(i <= 5) cout << t << endl << L[t].y << " " << L[t].l << " " << L[t].r << endl;
		// }

		for(int j = 0; j < sz(G[i + d]); j ++){
			int iu = G[i + d][j];
			int ul = L[iu].l, ur = L[iu].r;
			for(int k = 0; k < sz(G[i + d - 1]); k ++){
				int id = G[i - 1 + d][k];
				int dl = L[id].l, dr = L[id].r;
				int maxl = max(dl, ul), minr = min(dr, ur);
				if(maxl <= minr){
					near[iu][1].push_back(id);
					near[id][0].push_back(iu);
				}
			}
		}
	}
	queue<int> q;
	q.push(1); vis[1] = 1;
	while(!q.empty()){
		auto t = q.front(); q.pop();
	
		for(int i = 0; i < 2; i ++){
			for(auto nxt : near[t][i]){
				if(vis[nxt])  continue;

				q.push(nxt);
				vis[nxt] = 1;
			}
		}
	}
	ll ans = 0;
	for(int i = 1; i <= tot; i ++){
		if(vis[i] == 0){
	//		cout << L[i].l << " " << L[i].r << " " << L[i].y << endl;
			ans += 1ll * (L[i].r - L[i].l + 1);
		}
	}
	cout << ans << endl;

	return 0;
}
  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值