场次: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;
}