# 2020牛客暑期多校训练营（第一场）

## Contest Info

Solved A B C D E F G H I J
5 / 10 Ø - - - - O - Ø Ø O
• O 在比赛中通过
• Ø 赛后通过
• ! 尝试了但是失败了
• - 没有尝试

## Solutions

### A. B-Suffix Array

• 首先我们得到$$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;
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("\n");
}
}
int main() {
while (scanf("%d%s", &n, s) == 2) run();
return 0;
}


### F. Infinite String Comparision

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

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;
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" << '\n';
continue;
}
int need = (v + u - 1) / u;
if (need > npath) {
cout << "NaN" << '\n';
} 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 << '\n';
}
}
}
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

$$d_i$$都为$$1$$，那么这便是一个裸的一般图最大匹配。

• 两端的边各选一条，这种即是我们选择$$(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;
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" << '\n';
} else {
cout << "No" << '\n';
}
}
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 << "]\n";}
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
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 << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
init();
while (cin >> n) run();
return 0;
}

posted @ 2020-07-15 19:45  heyuhhh  阅读(257)  评论(0编辑  收藏  举报