牛客多校第五场题解
Away from College
https://ac.nowcoder.com/acm/contest/11256/A
B - Boxes
solved by Tryna.(-)
题意: 给出若干个盒子,盒子里面只有一个球,颜色为黑或白,每拆开一个盒子有一个花费,还可以通过花费一次来知道盒子内总共有多少黑球,求知道所有盒子中是什么球的花费的最小期望
题解:
- 不询问,所有盒子都开一遍,花费为
- 询问一遍,按花费从大到小开盒子,开到第
个盒子能直接判定的前提是后面全部同色并且第
个盒子与后面异色,所以一共需要
个球为
或者
,这两种情况出现的概率都为
,相加后为
,排序后处理一下前缀和即可
#include<bits/stdc++.h> using namespace std; #define dbg(x...) do { cout << #x << " -> "; err(x); } while (0) void err () { cout << endl;} template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);} #define ll long long #define ull unsigned long long #define LL __int128 #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #define pii pair<int, int> #define PII pair<ll, ll> #define fi first #define se second #define pb push_back #define eb emplace_back #define em emplace #define mp(a,b) make_pair(a,b) #define all(x) (x).begin(), (x).end() #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define PAUSE system("pause"); const double Pi = acos(-1.0); const double eps = 1e-10; const int maxn = 1e5 + 10; const int maxm = 6e2 + 10; const int mod = 998244353; // const int P = 131; const int S = 5; inline ll rd() { ll f = 0; ll x = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) f |= (ch == '-'); for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (f) x = -x; return x; } void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');} #define pt(x) out(x),puts("") inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;} inline void swap(int &a, int &b){int t = a; a = b; b = t;} inline ll min(ll a, ll b){return a < b ? a : b;} inline ll max(ll a, ll b){return a > b ? a : b;} ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;} int n; double c, a[maxn], sum[maxn]; void solve(){ scanf("%d %lf", &n, &c); for(int i = 1; i <= n; i++) { scanf("%lf", &a[i]); } sort(a + 1, a + 1 + n); for(int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i]; double minn = sum[n]; double base = 0.5; double ans = c; for(int i = n - 1; i >= 1; i--) { ans += base * sum[i]; base *= 0.5; } printf("%.6f\n", min(ans, minn)); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt", "w", stdout); int t = 1; // t = rd(); // scanf("%d", &t); // cin >> t; while(t--) solve(); PAUSE; return 0; }
D - Double Strings
题意:
从、
两个串中各选取两个相同大小的子集(就算字母相同,只要选取的位置不同,就算作不一样的),要求存在一个位置
,使得前面的字母都相同,并且第
位上从
选取的字母要小于
中选取的。问这样的子集有多少对。
思路:
很明显的题,我们需要做的是,当选取一对
,
时,若
,我就要计算出
,
之前能凑出多少个相等的字符串,之后能凑出多少个长度相同的字符串。
第一部分的和最长公共子序列很像。我可以用
去匹配
,用
去匹配
。因为是求数量,这里把取
改成相加。但是这样重复计算了既不用第
位,也不用第
位的情况,所以要减去
。如果
,我就能让它俩匹配,又能将
的情况加上去了。
第二部分,我们设,
,且
可以马上列出一个排列组合的式子
。
可以转换成
。我们带回原式子,发现是每次在
里选
个,b里选
个,这个总和就相当于在
里选
个。所以我们只需要计算
了。
比较大,组合数可以预处理一下,降个
的复杂度,不过加上去也能刚好卡过去。
#include <bits/stdc++.h> using namespace std; inline int rd() { int f = 0; int x = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) f |= (ch == '-'); for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (f) x = -x; return x; } typedef long long ll; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 5e3 + 7; const ll mod = 1e9 + 7; ll powmod(ll a, ll b) { ll res = 1; for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; } ll inv[N << 1], fac[N << 1]; ll C(int n, int m) { assert(n >= 0 && m >= 0); assert(n >= m); return fac[n] * inv[m] % mod * inv[n - m] % mod; } char a[N], b[N]; ll dp[N][N]; void solve() { scanf("%s", a + 1); scanf("%s", b + 1); int lena = strlen(a + 1), lenb = strlen(b + 1); assert(lena < N - 1 && lenb < N - 1); ll ans = 0; for (int i = 0; i < N; ++i) { dp[i][0] = dp[0][i] = 1; } for (int i = 1; i <= lena; ++i) { for (int j = 1; j <= lenb; ++j) { dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1]; dp[i][j] %= mod; if (a[i] == b[j]) dp[i][j] += dp[i - 1][j - 1]; dp[i][j] += mod; dp[i][j] %= mod; if (a[i] < b[j]) { ans += dp[i - 1][j - 1] * C(lena - i + lenb - j, lena - i) % mod; ans %= mod; } } } printf("%lld\n", ans); } int main() { fac[0] = fac[1] = 1; for (int i = 2; i < N * 2; ++i) { fac[i] = fac[i - 1] * (ll)i % mod; } inv[0] = 1; for (int i = 1; i < N * 2; ++i) { inv[i] = powmod(fac[i], mod - 2); } int t = 1; while (t--) solve(); return 0; }
F - Finding Points
题意: 给出一个凸包的顶点集,要求在凸包内找一点,令
为
中的最小值,求
的最大值
题解: 一种很明显的思路就是三分套三分,分别枚举坐标和
坐标,然后枚举
坐标的时候我认为需要确定在当前
值的情况下
的范围,不知道为啥很多码不用判断这个也能过,还是说这么枚举点一定在凸包内部
#include<bits/stdc++.h> using namespace std; #define dbg(x...) do { cout << #x << " -> "; err(x); } while (0) void err () { cout << endl;} template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);} #define ll long long #define ull unsigned long long #define LL __int128 #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #define pii pair<int, int> #define PII pair<ll, ll> #define fi first #define se second #define pb push_back #define eb emplace_back #define em emplace #define mp(a,b) make_pair(a,b) #define all(x) (x).begin(), (x).end() #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define PAUSE system("pause"); const double Pi = acos(-1.0); const double eps = 1e-10; const int maxn = 1e2 + 10; const int maxm = 6e2 + 10; const int mod = 998244353; const int S = 5; inline ll rd() { ll f = 0; ll x = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) f |= (ch == '-'); for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (f) x = -x; return x; } void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');} #define pt(x) out(x),puts("") inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;} inline void swap(int &a, int &b){int t = a; a = b; b = t;} inline ll min(ll a, ll b){return a < b ? a : b;} inline ll max(ll a, ll b){return a > b ? a : b;} ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;} int sgn(double x){ if(fabs(x) < eps) return 0; else return x < 0?-1:1; } struct Point{ double x,y; Point(){} Point(double x,double y):x(x),y(y){} Point operator + (Point B){return Point(x + B.x,y + B.y);} Point operator - (Point B){return Point(x - B.x,y - B.y);} Point operator * (double k){return Point(x*k,y*k);} Point operator / (double k){return Point(x/k,y/k);} bool operator == (Point B){return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;} bool operator < (const Point &b)const{ if(x == b.x) return y < b.y; return x < b.x; } }P[maxn]; struct Line{ Point p1,p2; Line(){}; Line(Point p1,Point p2):p1(p1),p2(p2){} }L[maxn]; double Dot(Point A, Point B){return A.x*B.x + A.y*B.y;} double Cross(Point A, Point B){return A.x*B.y - A.y*B.x;} bool Point_on_seg(Point p, Line v){ //0为不在线段上,1为在线段上 return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <=0; } bool Cross_segment1(Point a, Point b, Point c, Point d){ //两线段是否非规范相交 if(a == c || a == d) return 0; if(b == c || b == d) return 0; return max(a.x, b.x) >= min(c.x, d.x)&& max(c.x, d.x) >= min(a.x, b.x)&& max(a.y, b.y) >= min(c.y, d.y)&& max(c.y, d.y) >= min(a.y, b.y)&& sgn(Cross(b - a, c - a)) * sgn(Cross(b - a, d - a)) <= 0&& sgn(Cross(d - c, a - c)) * sgn(Cross(d - c, b - c)) <= 0; } Point Cross_point(Point a, Point b, Point c, Point d){ double s1 = Cross(b - a, c - a); double s2 = Cross(b - a, d - a); return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1); } int Point_in_polygon(Point pt, Point *p, int n){ //判断点和多边形的位置关系 for(int i = 0; i < n; i++){ if(p[i] == pt) return 3; //点在多边形的顶点上 } for(int i = 0; i < n; i++){ Line v = Line(p[i], p[(i + 1) % n]); if(Point_on_seg(pt, v)) return 2; //点在多边形边上 } int num = 0; for(int i = 0; i < n; i++){ int j = (i + 1) % n; int c = sgn(Cross(pt - p[j], p[i] - p[j])); int u = sgn(p[i].y - pt.y); int v = sgn(p[j].y - pt.y); if(c > 0 && u < 0 && v >= 0) num++; if(c < 0 && u >= 0 && v < 0) num--; } return num != 0; //1点在内部; 0点在外部 } int n; double getangle(double X1, double Y1, double X2, double Y2, double X3, double Y3) { double theta = atan2(X1 - X3, Y1 - Y3) - atan2(X2 - X3, Y2 - Y3); if(theta > Pi) theta -= Pi * 2; if (theta < -Pi) theta += Pi * 2; theta = abs(theta * 180.0 / Pi); return theta; } double cal(double x, double y) { double minn = 180.0; for(int i = 0; i < n; i++) { int id = (i + 1) % n; double angle = getangle(P[i].x, P[i].y, P[id].x, P[id].y, x, y); minn = min(minn, angle); } return minn; } double ly, ry, L1, R; double f(double x) { Line l = Line({x, 10000}, {x, -10000}); double ly = INF, ry = -INF; for(int i = 0; i < n; i++) { if(Cross_segment1(l.p1, l.p2, L[i].p1, L[i].p2) != 1) continue; Point tmp = Cross_point(l.p1, l.p2, L[i].p1, L[i].p2); ly = min(ly, tmp.y); ry = max(ry, tmp.y); } double midl, midr; L1 = ly, R = ry; while(R - L1 > eps) { midl = L1 + (R - L1) / 3.0; midr = R - (R - L1) / 3.0; if(cal(x, midl) > cal(x, midr)) R = midr; else L1 = midl; } return cal(x, L1); } void solve(){ scanf("%d", &n); double lx = inf, rx = -inf; ly = inf, ry = -inf; for(int i = 0; i < n; i++) { scanf("%lf %lf", &P[i].x, &P[i].y); lx = min(lx, P[i].x); rx = max(rx, P[i].x); ly = min(ly, P[i].y); ry = max(ry, P[i].y); } for(int i = 0; i < n; i++) { L[i] = Line(P[i], P[(i + 1) % n]); } double midl, midr; while(rx - lx > eps){ midl = lx + (rx - lx)/ 3.0; midr = rx - (rx - lx)/ 3.0; if(f(midl) > f(midr)) rx = midr; else lx = midl; } printf("%.14f\n", f(lx)); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt", "w", stdout); int t = 1; // t = rd(); // scanf("%d", &t); // cin >> t; while(t--) solve(); return 0; }
题解: 用三分套三分能过那么用模拟退火肯定也能过。但我的模拟退火似乎不是很优秀,在小样例并且点与点之间的距离较远时误差会较大,测试了一下平均六发过一次,可能算欧了。
#include<bits/stdc++.h> using namespace std; #define dbg(x...) do { cout << #x << " -> "; err(x); } while (0) void err () { cout << endl;} template <class T, class... Ts> void err(const T& arg, const Ts&... args) { cout << arg << ' '; err(args...);} #define ll long long #define ull unsigned long long #define LL __int128 #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f3f3f #define pii pair<int, int> #define PII pair<ll, ll> #define fi first #define se second #define pb push_back #define eb emplace_back #define em emplace #define mp(a,b) make_pair(a,b) #define all(x) (x).begin(), (x).end() #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #define PAUSE system("pause"); const double Pi = acos(-1.0); const double eps = 1e-10; const int maxn = 1e2 + 10; const int maxm = 6e2 + 10; const int mod = 998244353; const double startT = 100000; const int S = 5; const int dx[]= {-1,-1,-1,0,0,1,1,1}; const int dy[]= {-1,0,1,-1,1,-1,0,1}; inline ll rd() { ll f = 0; ll x = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) f |= (ch == '-'); for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (f) x = -x; return x; } void out(ll a) {if(a<0)putchar('-'),a=-a;if(a>=10)out(a/10);putchar(a%10+'0');} #define pt(x) out(x),puts("") inline void swap(ll &a, ll &b){ll t = a; a = b; b = t;} inline void swap(int &a, int &b){int t = a; a = b; b = t;} inline ll min(ll a, ll b){return a < b ? a : b;} inline ll max(ll a, ll b){return a > b ? a : b;} ll qpow(ll n,ll k,ll mod) {ll ans=1;while(k){if(k&1)ans=ans*n%mod;n=n*n%mod;k>>=1;}return ans%mod;} int sgn(double x){ if(fabs(x) < eps) return 0; else return x < 0?-1:1; } struct Point{ double x,y; Point(){} Point(double x,double y):x(x),y(y){} Point operator + (Point B){return Point(x + B.x,y + B.y);} Point operator - (Point B){return Point(x - B.x,y - B.y);} Point operator * (double k){return Point(x*k,y*k);} Point operator / (double k){return Point(x/k,y/k);} bool operator == (Point B){return sgn(x - B.x) == 0 && sgn(y - B.y) == 0;} bool operator < (const Point &b)const{ if(x == b.x) return y < b.y; return x < b.x; } }P[maxn]; int n; double getangle(double X1, double Y1, double X2, double Y2, double X3, double Y3) { double theta = atan2(X1 - X3, Y1 - Y3) - atan2(X2 - X3, Y2 - Y3); if(theta > Pi) theta -= Pi * 2; if (theta < -Pi) theta += Pi * 2; theta = abs(theta * 180.0 / Pi); return theta; } double cal(double x, double y) { double minn = 180.0; for(int i = 0; i < n; i++) { int id = (i + 1) % n; double angle = getangle(P[i].x, P[i].y, P[id].x, P[id].y, x, y); minn = min(minn, angle); } return minn; } void solve(){ scanf("%d", &n); double maxx = -inf, minn = inf; for(int i = 0; i < n; i++) { scanf("%lf %lf", &P[i].x, &P[i].y); maxx = max(maxx, max(P[i].x, P[i].y)); minn = min(minn, min(P[i].x, P[i].y)); } mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); int times = 5; double bas = maxx - minn + 1; if(n <= 4) times = 80; else if(n <= 5) times = 70; else if(n <= 10) { times = 25; } else if(n <= 20) times = 10; double T = startT, delta = 0.985, x, y; double ans = -inf; x = 0, y = 0; double base = 0.02 / times; while(times--) { T = startT, delta = 0.980 + times * base - 0.001; while(T > eps) { int a = rng() % 2; int b = rng() % (int)startT; double next_x, next_y; if(a == 0) next_x = x + b * T; else next_x = x - b * T; a = rng() % 2, b = rng() % (int)startT; if(a == 0) next_y = y + b * T; else next_y = y - b * T; if(cal(x, y) < cal(next_x, next_y)) { x = next_x; y = next_y; } ans = max(ans, cal(x, y)); T *= delta; } // x = 0, y = 0; T = startT, delta = 0.98 + times * base - 0.001; while(T > eps) { int a = rng() % 2; int b = rng() % 500; double next_x, next_y; if(a == 0) next_x = x + b * T; else next_x = x - b * T; a = rng() % 2, b = rng() % 500; if(a == 0) next_y = y + b * T; else next_y = y - b * T; if(cal(x, y) < cal(next_x, next_y)) { x = next_x; y = next_y; } ans = max(ans, cal(x, y)); T *= delta; } } // printf("%.5lf %.5lf\n", x, y); printf("%.14lf\n", ans); } int main() { // freopen("in.txt","r",stdin); // freopen("out.txt", "w", stdout); int t = 1; // cin >> t; while(t--) solve(); return 0; }
H - Holding Two
题意: 构造一个的
矩阵,满足没有三个连续(行、列、斜线)的元素相同
题解:
这样构造即可
#include <bits/stdc++.h> using namespace std; #define endl "\n" #define dbg(x...) do { cerr << "\033[32;1m" << #x << " -> "; err(x);} while(0) void err() { cerr << "\033[39;0m" << endl;} template<class T, class... Ts> void err(const T& arg, const Ts&... args) { cerr << arg << " "; err(args...);} typedef long long ll; const int maxn = 1e3 + 7; int n, m, a[maxn][maxn]; void run() { scanf("%d %d", &n, &m); for(int i = 1; i <= n; i++) { int tmp = 1; if(i % 2 == 0) tmp = 0; for(int j = 1; j <= m; j += 2) { a[i][j] = a[i][j + 1] = tmp; tmp = 1 - tmp; } } for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { printf("%d", a[i][j]); } puts(""); } } int main() { int t = 1; // scanf("%d", &t); while (t--) run(); return 0; }
J - Jewels
题意:
三维坐标,人在,每整数秒可以选择一个宝石,花费为宝石到人的距离的平方,在第
秒时宝石
的的坐标为
,问最小花费。
思路:,
不会变化,直接算入答案。
都为正,答案只会越来越大,所以每秒都要取一个,就转换成了
模型。
费用流麻了,拉了份
行的
板子才能过。
#include <bits/stdc++.h> using namespace std; #define endl "\n" #define dbg(x...) do { cerr << "\033[32;1m" << #x << " -> "; err(x);} while(0) void err() { cerr << "\033[39;0m" << endl;} template<class T, class... Ts> void err(const T& arg, const Ts&... args) { cerr << arg << " "; err(args...);} typedef long long ll; const int N = 3e2 + 7; const ll INF = 0x3f3f3f3f3f3f3f3f; ll cap[N][N]; ll slack[N], wx[N], wy[N]; int n, m, k, tot; int match[N], pre[N], used[N], vis[N]; void bfs(int u) { ll px, py = 0, yy = 0, d; memset(pre, 0, sizeof(pre)); memset(slack, 0x3f, sizeof(slack)); used[py] = u; while (used[py]) { px = used[py] ,d = INF, vis[py] = 1; for (int i = 1; i <= tot; ++i) { if (!vis[i]) { if (slack[i] > wx[px] + wy[i] - cap[px][i]) { slack[i] = wx[px] + wy[i] - cap[px][i], pre[i] = py; } if (slack[i] < d) d = slack[i], yy = i; } } for (int i = 0; i <= tot; ++i) { if (vis[i]) wx[used[i]] -= d, wy[i] += d; else slack[i] -= d; } py = yy; } while (py) used[py] = used[pre[py]], py = pre[py]; } ll km() { memset(wx, 0, sizeof(wx)); memset(wy, 0, sizeof(wy)); memset(used, 0, sizeof(used)); for (int i = 1; i <= tot; ++i) { memset(vis, 0, sizeof(vis)); bfs(i); } ll ans = 0; for (int i = 1; i <= tot; ++i) { ans += cap[used[i]][i], match[used[i]] = i; } return ans; } void run() { scanf("%d", &n); ll ans = 0; tot = n; for (int i = 1; i <= n; ++i) { ll x, y, z, v; scanf("%lld %lld %lld %lld", &x, &y, &z, &v); ans += x * x; ans += y * y; for (int j = 1; j <= n; ++j) { cap[j][i] = -((ll)(j - 1) * v + z) * ((ll)(j - 1) * v + z); } } ans += -km(); printf("%lld\n", ans); } int main() { int t = 1; while (t--) run(); return 0; }
K - King of Range
题意:
给定序列,求多有少对不同的数对(
,
),使得区间内的最大值和最小值的差值大于
。
思路:
假设我们从出发,一路向右走,走到
时刚好满足条件,那么
~
都是满足要求的。这时候把l向右移动一位,能正好满足它的
肯定会大于等于先前求得的
,符合决策单调性。所以用个双指针,区间求最大最小值就行了。
序列是给定的,但是有多组询问,带个一直跑很容易
。所以上
就能实现
求区间最大最小值了。
之前骂RMQ没用有多狠,现在就有多惨
#include <bits/stdc++.h> using namespace std; inline int rd() { int f = 0; int x = 0; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) f |= (ch == '-'); for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (f) x = -x; return x; } typedef long long ll; const int inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f; const int N = 1e5 + 7; int n, m, k; int mx[N][20], mn[N][20]; void st(int n){ for(int l=1;(1<<l)<=n;l++){ for(int i=1;i+(1<<l)-1<=n;i++){ mx[i][l]=max(mx[i][l-1],mx[i+(1<<(l-1))][l-1]); mn[i][l]=min(mn[i][l-1],mn[i+(1<<(l-1))][l-1]); } } } int RMQ(int l,int r,int w) { int k = __lg(r - l + 1); int ans; if(!w) ans=max(mx[l][k],mx[r-(1<<k)+1][k]); else ans=min(mn[l][k],mn[r-(1<<k)+1][k]); return ans; } bool check(int l, int r) { return RMQ(l, r, 0) - RMQ(l, r, 1) > k; } void solve() { n = rd(), m = rd(); for (int i = 1; i <= n; ++i) { mx[i][0] = rd(); mn[i][0] = mx[i][0]; } st(n); while (m--) { ll ans = 0; k = rd(); int head = 1, rear = 1; while (head <= n) { while (rear <= n && !check(head, rear)) rear++; if (rear > n) { head++; continue; } ans += n - rear + 1; head++; } printf("%lld\n", ans); } } int main() { int t = 1; while (t--) solve(); return 0; }