目录
Contest Info
Solved | A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|---|
5 / 10 | Ø | - | - | - | - | O | - | Ø | Ø | O |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. B-Suffix Array
题意:
给定一个只包含(a,b)的字符串,现有一函数(B),字符串通过(B)作用能得到一个新的数组(b),其中(displaystyle b_i=min_{1leq j<i,t_j=t_i}(i-j))。
现在找到一个排列(p),使得(B(t_{p_i...p_n})<B_{p_{i+1}...p_n},1leq i<n)都成立,这里是进行字典序比较。
思路:
题解的做法是关于一个结论,不过较难想到,这里说一下一种比较容易想的做法:
- 首先我们得到(b)数组。
- 对于所有的后缀(i...n),我们找到(R[i])表示当前后缀第(2)个(0)的位置,比如后缀以(aaaab)开头时(R[i]=5)。
- 那么显然可以按照(R[i]-i)从小到大排序,对于(R[i]-i=R[j]-j)相等的情况,我们只需要按照(j+1...n)后缀从小到大排序即可。
容易证明这样排序得到的顺序符合条件。
细节见代码:
Code
// Author : heyuhhh
// Created Time : 2020/07/15 14:46:06
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
struct SA{ //sa:1...n Rank:0...n-1
int sa[N], c[N], Rank[N], t1[N], t2[N];
int n; //length
void da(int* s, int m){
s[++n] = 0; //!! s[n]=0
int* x = t1, *y = t2;
for(int i = 0; i < m; ++i) c[i] = 0;
for(int i = 0; i < n; ++i) c[x[i] = s[i]]++;
for(int i = 1; i < m; ++i) c[i] += c[i - 1] ;
for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0 ;
for(int i = n - k; i < n; ++i) y[p++] = i ;
for(int i = 0; i < n; ++i) if(sa[i] >= k) y[p++] =sa[i] - k;
for(int i = 0; i < m; ++i) c[i] = 0;
for(int i = 0; i < n; ++i) c[x[y[i]]]++;
for(int i = 1; i < m; ++i) c[i] += c[i - 1];
for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ;
swap(x , y); p = 1; x[sa[0]] = 0;
for(int i = 1; i < n; ++i)
x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p - 1 : p++;
if(p >= n) break ;
m = p;
}
n--;
for(int i = 0; i <= n; ++i) Rank[sa[i]] = i;
}
}suf;
int n;
char s[N];
int R[N], a[N];
void run() {
suf.n = n;
for (int i = 0, j; i < n; i = j + 1) {
j = i;
while (j + 1 < n && s[j + 1] == s[j]) ++j;
for (int k = i; k <= j; k++) {
R[k] = j + 1;
}
}
int pre[2] = {-1, -1};
for (int i = 0; i < n; ++i) {
if (pre[s[i] - 'a'] == -1) {
a[i] = 0;
} else {
a[i] = i - pre[s[i] - 'a'];
}
pre[s[i] - 'a'] = i;
}
suf.da(a, n + 2);
vector<int> ord(n);
iota(all(ord), 0);
auto get = [&] (int i) {
return i >= n ? -1 : suf.Rank[i];
};
sort(all(ord), [&] (int i, int j) {
int len1 = R[i] - i, len2 = R[j] - j;
if (len1 != len2) {
return len1 < len2;
}
if (R[i] >= n || R[j] >= n) {
return R[i] >= n;
}
return get(R[i] + 1) < get(R[j] + 1);
});
for (int i = 0; i < n; ++i) {
printf("%d", ord[i] + 1);
if (i < n - 1) printf(" ");
else printf("
");
}
}
int main() {
while (scanf("%d%s", &n, s) == 2) run();
return 0;
}
F. Infinite String Comparision
将两个串倍长比较即可,其实只需要比较到(|a|+|b|-gcd(|a|,|b|))即可。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+233;
long long n,k,t;
int main(){
string a,b;
while(cin>>a>>b){
int i=0,j=0;
int len1=a.size();
int len2=b.size();
int g=__gcd(len1,len2);
if(len1==len2){
if(a==b) cout<<"="<<endl;
if(a>b) cout<<">"<<endl;
if(a<b) cout<<"<"<<endl;
continue;
}
int f = 0;
int maxlen=10*max(len1, len2);
while(maxlen--){
if(a[i]>b[j]) {
f=1;
break;
}
if(a[i]<b[j]){
f=2;
break;
}
i++;
j++;
i%=len1;
j%=len2;
}
if(f==1) cout<<">"<<endl;
if(f==2) cout<<"<"<<endl;
if(!f) cout<<"="<<endl;
}
return 0;
}
H. Minimum-cost Flow
因为给出的就是一个网络,具有网络的性质。
所以我们将网络看作容量为单位(1)的网络,因为最多增广(O(m))次,预处理关于最小费用的前缀和即可。
询问时找到最小增广次数过后可以直接(O(1))计算。
注意分子分母的计算,分母为(v),而分子都应乘上(u)。
Code
// Author : heyuhhh
// Created Time : 2020/07/12 14:24:44
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 100 + 5, M = 200 + 5;
#define INF 0x3f3f3f3f
struct E {
int from, to, cp, v;
E() {}
E(int f, int t, int cp, int v) : from(f), to(t), cp(cp), v(v) {}
};
int f[M];
struct MCMF {
int n, m, s, t;
vector<E> edges;
vector<int> G[N];
bool inq[N];
int d[N], p[N], a[M];
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
for(int i = 0; i <= n; i++) G[i].clear();
edges.clear(); m = 0;
}
void addedge(int from, int to, int cap, int cost) {
edges.emplace_back(from, to, cap, cost);
edges.emplace_back(to, from, 0, -cost);
G[from].push_back(m++);
G[to].push_back(m++);
}
bool BellmanFord(int &flow, int &cost) {
for(int i = 0; i <= n; i++) d[i] = INF;
memset(inq, 0, sizeof inq);
d[s] = 0, a[s] = INF, inq[s] = true;
queue<int> Q; Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for (int& idx: G[u]) {
E &e = edges[idx];
if (e.cp && d[e.to] > d[u] + e.v) {
d[e.to] = d[u] + e.v;
p[e.to] = idx;
a[e.to] = min(a[u], e.cp);
if (!inq[e.to]) {
Q.push(e.to);
inq[e.to] = true;
}
}
}
}
if (d[t] == INF) return false;
flow += a[t];
cost += a[t] * d[t];
int u = t;
while (u != s) {
edges[p[u]].cp -= a[t];
edges[p[u] ^ 1].cp += a[t];
u = edges[p[u]].from;
}
return true;
}
void go(int& flow) {
flow = 0;
int cost = 0;
while (BellmanFord(flow, cost)) {
f[flow] = f[flow - 1] + cost;
cost = 0;
};
}
} MM;
int n, m;
void run() {
MM.init(n, 1, n);
for (int i = 0; i < m; i++) {
int u, v, w; cin >> u >> v >> w;
MM.addedge(u, v, 1, w);
}
int npath;
MM.go(npath);
int q; cin >> q;
while (q--) {
int u, v; cin >> u >> v;
if (u == 0) {
cout << "NaN" << '
';
continue;
}
int need = (v + u - 1) / u;
if (need > npath) {
cout << "NaN" << '
';
} else {
assert(u > 0 && v >= u);
assert(need > 0);
ll fz = 1ll * f[need - 1] * u + 1ll * (f[need] - f[need - 1]) * (v - (need - 1) * u);
ll fm = v;
ll g = __gcd(fz, fm);
assert(g > 0);
fz /= g, fm /= g;
cout << fz << "/" << fm << '
';
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while (cin >> n >> m) run();
return 0;
}
I. 1 or 2
题意:
给出一张(n)个点(m)条边的无向图,回答是否能找到一个子图使得每个点的度数都恰好为(d_i)。
思路:
是一道(hduoj)上的原题,做法很巧妙。
若(d_i)都为(1),那么这便是一个裸的一般图最大匹配。
考虑(d_i>1)的情况,其实可以得到启示:对度数进行拆点。
这样对于两个点(u,v),将他们按照度数拆为若干个点,若现在存在一条边连接(u,v),那么一端连接所有的(u),一端连接所有的(v)即可。
最后在匹配中,因为是最大匹配,那么只会有两种情况:
- 两端的边各选一条,这种即是我们选择((u,v))这条边保留,对应度数减少(1);
- 选择中间的边,这即是在图中删除这条边,度数不发生变化。
所以正确性就比较显然了。
细节见代码:
Code
// Author : heyuhhh
// Created Time : 2020/07/12 20:50:24
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 500 + 5, M = 100 + 5;
int n, m;
int d[N];
struct mf {
// Time complexity: O(n^3)
// 1-based Vertex index
// match[x]: vertex matched with x
// N: numbers of vertex
int vis[N], par[N], orig[N], match[N], aux[N], t, n;
vector<int> conn[N];
queue<int> Q;
void add_edge(int u, int v) {
conn[u].push_back(v);
conn[v].push_back(u);
}
void init(int _n) {
n = _n;
t = 0;
for (int i = 0; i <= n; ++i) {
conn[i].clear();
match[i] = aux[i] = par[i] = 0;
}
}
void augment(int u, int v) {
int pv = v, nv;
do {
pv = par[v];
nv = match[pv];
match[v] = pv;
match[pv] = v;
v = nv;
} while (u != pv);
}
int lca(int v, int w) {
++t;
while (true) {
if (v) {
if (aux[v] == t) return v;
aux[v] = t;
v = orig[par[match[v]]];
}
swap(v, w);
}
}
void blossom(int v, int w, int a) {
while (orig[v] != a) {
par[v] = w;
w = match[v];
if (vis[w] == 1) Q.push(w), vis[w] = 0;
orig[v] = orig[w] = a;
v = par[w];
}
}
bool bfs(int u) {
fill(vis + 1, vis + 1 + n, -1);
iota(orig + 1, orig + n + 1, 1);
Q = queue<int>();
Q.push(u);
vis[u] = 0;
while (!Q.empty()) {
int v = Q.front();
Q.pop();
for (int x : conn[v]) {
if (vis[x] == -1) {
par[x] = v;
vis[x] = 1;
if (!match[x]) return augment(u, x), true;
Q.push(match[x]);
vis[match[x]] = 0;
} else if (vis[x] == 0 && orig[v] != orig[x]) {
int a = lca(orig[v], orig[x]);
blossom(x, v, a);
blossom(v, x, a);
}
}
}
return false;
}
int Match() {
int ans = 0;
// find random matching (not necessary, constant improvement)
vector<int> V(n - 1);
iota(V.begin(), V.end(), 1);
shuffle(V.begin(), V.end(), mt19937(61471));
for (auto x : V)
if (!match[x]) {
for (auto y : conn[x])
if (!match[y]) {
match[x] = y, match[y] = x;
++ans;
break;
}
}
for (int i = 1; i <= n; ++i)
if (!match[i] && bfs(i)) ++ans;
return ans;
}
} mf;
void run() {
mf.init(500);
int deg = 0;
for (int i = 1; i <= n; i++) {
cin >> d[i];
deg += d[i];
}
int nxt = 150;
for (int i = 1; i <= m; i++) {
int u, v; cin >> u >> v;
mf.add_edge(nxt, nxt + 1);
for (int j = 0; j < d[u]; j++) {
mf.add_edge(2 * u + j, nxt);
}
for (int j = 0; j < d[v]; j++) {
mf.add_edge(nxt + 1, 2 * v + j);
}
nxt += 2;
}
int Max = mf.Match();
if (deg == 2 * Max - 2 * m) {
cout << "Yes" << '
';
} else {
cout << "No" << '
';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while (cin >> n >> m) run();
return 0;
}
J. Easy Integration
oeis即可。
也可以进行相关数学推导,会涉及到分部积分。可以直接通过分部积分,也可以三角换元,也可以直接利用相关公式。我就不一一赘述了。
Code
// Author : heyuhhh
// Created Time : 2020/07/12 12:16:05
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define Local
void err(int x) {cerr << x;}
void err(long long x) {cerr << x;}
void err(double x) {cerr << x;}
void err(char x) {cerr << '"' << x << '"';}
void err(const string &x) {cerr << '"' << x << '"';}
void _print() {cerr << "]
";}
template<typename T, typename V>
void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
template<typename T>
void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
template <typename T, typename... V>
void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
#ifdef Local
#define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
#else
#define dbg(x...)
#endif
//head
const int N = 2e6 + 5, MOD = 998244353;
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if (b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int f[N];
int fac[N];
void init() {
fac[0] = 1;
for (int i = 1; i < N; i++) {
fac[i] = 1ll * fac[i - 1] * i % MOD;
}
}
int n;
void run() {
int ans = fac[2 * n + 1];
int res = 1ll * fac[n] * fac[n] % MOD;
ans = 1ll * ans * qpow(res, MOD - 2) % MOD;
ans = qpow(ans, MOD - 2);
cout << ans << '
';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
while (cin >> n) run();
return 0;
}