2022牛客多校第二场部分题解
G.Link with Monotonic Subsequence
题目简述:
一个n的排列p,求最小的max(lis(p),lds(p))
思路:
最大值应该为sqrt(n)上去整,然后构造即可如:4 3 2 1 8 7 6 5 11 10 9
代码:
#include <bits/stdc++.h>
#define int long long
int _ = 0, Case = 1;
using namespace std;
#define all(v) begin(v),end(v)
#define nline '\n'
#define SZ(v) (int) v.size()
void solve(int Case) {
int n;
cin >> n;
int t = ceil(sqrt(n));
int k = n / t;
for (int i = 1; i <= k; i++) {
int idx = i * t;
for (int j = idx; j > idx - t; j--) cout << j << ' ';
}
if (n % t) {
int x = n % t;
int s = n;
for (; x--;) cout << s-- << ' ';
}
cout << nline;
}
signed main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
for (cin >> _, Case = 1; Case <= _; Case++)
solve(Case);
return 0;
}
K.Link with Bracket Sequence I
题目简述:
给了一个长度为m的字符串S,问有多少长度为n的括号序列,并且存在一个子序列是S
思路:
数据范围很明显是n3的dp,首先容易想到的是前i个中匹配到S的前j的方案数,
因为转移要合法,考虑到他是括号序列,括号序列在任意时刻都满足前缀的左括号数量大于等于右括号数量;
那么状态表示就是f[i][j][k]表示当前选择到前i个,匹配到s的前j个,并且选择了k个左括号,
状态转移就是要么左括号要么右括号,注意j和k枚举的范围。
if (j == 0) {
f[i][j][k] = f[i - 1][j][k - 1] + f[i - 1][j][k];
} else if (s[j] == '(') {
f[i][j][k] = f[i - 1][j - 1][k - 1] + f[i - 1][j][k] ;
} else {
f[i][j][k] = f[i - 1][j - 1][k] + f[i - 1][j][k - 1] ;
}
代码:
#include <bits/stdc++.h>
#define int long long
int _ = 0, Case = 1;
using namespace std;
#define all(v) begin(v),end(v)
#define nline '\n'
#define SZ(v) (int) v.size()
const int N = 250, mod = 1e9 + 7;
int f[N][N][N];
char s[N];
void solve(int Case) {
int n, m;
cin >> m >> n;
cin >> (s + 1);
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
for (int k = 0; k <= n; k++) {
f[i][j][k] = 0;
}
}
}
f[0][0][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= min(i, m); j++) {
for (int k = ((i + 1 ) / 2); k <= i; k++) {
if (j == 0) {
f[i][j][k] = f[i - 1][j][k - 1] + f[i - 1][j][k];
} else if (s[j] == '(') {
f[i][j][k] = f[i - 1][j - 1][k - 1] + f[i - 1][j][k] ;
} else {
f[i][j][k] = f[i - 1][j - 1][k] + f[i - 1][j][k - 1] ;
}
f[i][j][k] %= mod;
}
}
}
cout << f[n][m][n / 2] << nline;
}
signed main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
for (cin >> _, Case = 1; Case <= _; Case++)
solve(Case);
return 0;
}
D.Link with Game Glitch
题目简述:
有n个物品,m组关系,比如b,d物品之间,a个b可以转换成c个d,但是有可能会出现无限转换,现在要求一个w使得ab=cw*d,并且不能出现无限循环,求最大的w
思路:
ab=cwd,假设起初每个物品都有一个,建边b->d=a/c/w,表示一个d可以变成可以由a/c/w个b变过来,然后就很明显的连乘,看是否形成环,但是由于好多小于1的小数,
连乘容易丢失精度,可以选择log,log(ab)就变成log(a)+log(b)
代码:
#include <bits/stdc++.h>
#define int long long
int _ = 0, Case = 1;
using namespace std;
#define all(v) begin(v),end(v)
#define nline '\n'
#define SZ(v) (int) v.size()
const int N = 2020;
int w[N];
using PII = pair<int, double>;
vector<PII> h[N];
int n, m;
bool vis[N];
double d[N];
int cnt[N];
bool check(double x) {
x = log(x);
queue<int>q;
for (int i = 1; i <= n; i++) vis[i] = 1, d[i] = 0, cnt[i] = 0, q.push(i);
while (q.size()) {
auto t = q.front();
q.pop();
vis[t] = 0;
for (auto [i, w] : h[t]) {
if (d[i] > d[t] +w -x) {
d[i] = d[t] + w - x;
cnt[i] = cnt[t] + 1;
if (cnt[i] >= n) return false;
if (!vis[i]) {
vis[i] = 1;
q.push(i);
}
}
}
}
return true;
}
void solve(int Case) {
cin >> n >> m;
for (int i = 1; i <= m; i++) {
double a, c;
int b, d;
cin >> a >> b >> c >> d;
h[b].push_back({d, log(a/ c)});
}
double l = 0, r = 5;
for (int i = 0; i < 60; i++) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
cout << l << nline;
}
signed main() {
ios::sync_with_stdio(false); cin.tie(nullptr);
// for (cin>>_, Case = 1; Case <= _; Case++)
solve(Case);
return 0;
}