2020牛客暑期多校训练营(第一场)

先把AC的代码放上来吧,题解慢慢补。
很期待人退出第二季呢

赛后总结

赛中一人写了4题,有很大水分。
F作为签到题也是一个结论,虽然并没有猜出来但是也水过了。
J题是伽马函数,(虽然看着像二项式展开),计算了前几项OEIS也水过了,估计现场赛会推给队友吧。
I题是HDOJ3551一般图最大匹配的简化版?我用最大流水过了,出题人说最大流做法是错误的。
H是拉格朗日插值?我用费用流+map水过了……

A. B-Suffix Array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
const int MAXN = 100010;
struct SuffixArray
{//倍增算法 O(nlogn)
int n;//包括末尾0在内的字符数
int Rank[MAXN], height[MAXN], sa[MAXN], r[MAXN];
//rnk从0开始
//sa从1开始,因为最后一个字符(最小的)排在第0位
//height从1开始,因为表示的是sa[i - 1]和sa[i]
int wa[MAXN], wb[MAXN], wv[MAXN], ws_[MAXN];
//Suffix函数的参数m代表字符串中字符的取值范围,是基数排序的一个参数,如果原序列都是字母可以直接取128,如果原序列本身都是整数的话,则m可以取比最大的整数大1的值
//待排序的字符串放在r数组中,从r[0]到r[n-1],长度为n
//为了方便比较大小,可以在字符串后面添加一个字符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小
//同上,为了函数操作的方便,约定除r[n-1]外所有的r[i]都大于0,r[n-1]=0
//函数结束后,结果放在sa数组中,从sa[0]到sa[n-1],sa[0]=n-1
void Suffix(int m)
{
int i, j, k, *x = wa, *y = wb, *t;
//对长度为1的字符串排序
//一般来说,在字符串的题目中,r的最大值不会很大,所以这里使用了基数排序
//如果r的最大值很大,那么把这段代码改成快速排序
for(i = 0; i < m; ++i) ws_[i] = 0;
for(i = 0; i < n; ++i) ws_[x[i] = r[i]]++;//统计字符的个数
for(i = 1; i < m; ++i) ws_[i] += ws_[i - 1];//统计不大于字符i的字符个数
for(i = n - 1; i >= 0; --i) sa[--ws_[x[i]]] = i;//计算字符排名
//基数排序
//x数组保存的值相当于是rank值
for(j = 1, k = 1; k < n; j<<=1, m = k)
{
//j是当前字符串的长度,数组y保存的是对第二关键字排序的结果
//第二关键字排序
for(k = 0, i = n - j; i < n; ++i) y[k++] = i;//第二关键字为0的排在前面
for(i = 0; i < n; ++i) if(sa[i] >= j) y[k++] = sa[i] - j;//长度为j的子串sa[i]应该是长度为2 * j的子串sa[i] - j的后缀(第二关键字),对所有的长度为2 * j的子串根据第二关键字来排序
for(i = 0; i < n; ++i) wv[i] = x[y[i]];//提取第一关键字
//按第一关键字排序 (原理同对长度为1的字符串排序)
for(i = 0; i < m; ++i) ws_[i] = 0;
for(i = 0; i < n; ++i) ws_[wv[i]]++;
for(i = 1; i < m; ++i) ws_[i] += ws_[i - 1];
for(i = n - 1; i >= 0; --i) sa[--ws_[wv[i]]] = y[i];//按第一关键字,计算出了长度为2 * j的子串排名情况
//此时数组x是长度为j的子串的排名情况,数组y仍是根据第二关键字排序后的结果
//计算长度为2 * j的子串的排名情况,保存到数组x
t = x;
x = y;
y = t;
for(x[sa[0]] = 0, i = k = 1; i < n; ++i)
x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) ? k - 1 : k++;
//若长度为2 * j的子串sa[i]与sa[i - 1]完全相同,则他们有相同的排名
}
}
void build_height()
{
int i, j, k = 0;
for(i = 0; i < n; i++)
Rank[sa[i]] = i;
for(i = 0; i < n; i++)
{
if(k) k--;
int j = sa[Rank[i]-1];
while(r[i+k] == r[j+k]) k++;
height[Rank[i]] = k;
}
}
void debug()
{
printf(" 名次 下标 height 后缀串\n");
for(int i=0;i<n;i++)
{
printf("%5d%5d%5d ",i,sa[i],height[i]);
for(int j=sa[i];j<n;j++)
{
printf("%-3d ",r[j]);
}
printf("\n");
}
}
} sa;
signed main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
int n;
string s;
while(cin>>n>>s)
{
int a,b;
a=b=n;
sa.n=n+1;
sa.r[n]=n+1;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='a')
{
if(a<n)
sa.r[i]=a-i;
else
sa.r[i]=n;
a=i;
}
else{
if(b<n)
sa.r[i]=b-i;
else
sa.r[i]=n;
b=i;
}
}
// for(int i=0;i<=n;i++)
// cout<<sa.r[i]<<' ';
// cout<<endl;
sa.Suffix(n+2);
for(int i=n;i>=0;i--)
if(sa.sa[i]!=n)
cout<<sa.sa[i]+1<<' ';
cout<<endl;
}
return 0;
}

B. Infinite Tree

线段树维护质因子分解构建虚树跑换根dp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
//#define DEBUG//#define lowbit(x) ((x) & -(x))//<<setiosflags(ios::fixed)<<setprecision(9)
void read(){}
template<typename T,typename... T2>inline void read(T &x,T2 &... oth) {
x=0; int ch=getchar(),f=0;
while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
if(f)x=-x;
read(oth...);
}
struct BinaryIndexedsTree
{
int n;
ll c[maxn],sum[maxn];//c维护差分数组,sum维护i*c[i]的前缀和
inline int lowbit(int x)
{
return x & (-x);
}
void build(int n)//a是原数组,n是元素个数
{
this->n=n;
memset(c,0,sizeof(c));
memset(sum,0,sizeof(sum));
// for(int i=1;i<=n;i++)
// add(i,a[i]-a[i-1]);//构建差分c数组
}
void add(int k,ll val)//修改:a[k]加上val,直接在c数组上操作
{//请使用区间更新
for(int i=k;i<=n;i+=lowbit(i))//从叶子一直更新到父节点
c[i]+=val,sum[i]+=val*(k-1);//每个节点都加上val
}
void radd(int l,int r,ll val)//区间更新
{
add(l,val);//把l之后所有的点都更新一遍
add(r+1,-val);//因为r之后的点是不用更新的,但是多更新了,所以要每个点都-val;
}
ll query(int k)//查询:c[i]前缀和的前缀和,即a[i]的前缀和
{//节点i的长度为lowbit(i)
ll ret=0;
for(int i=k;i;i-=lowbit(i))
ret+=k*c[i]-sum[i];
return ret;
}
} bit;
int mindiv[maxn]={0,1},depth[maxn],lcadep[maxn];
int w[maxn],m,n,stk[maxn],top=0;
vector<int> G[maxn];
inline void add(int u,int v)
{
G[u].push_back(v);
G[v].push_back(u);
}
void build()
{
n=m;
stk[top=1]=1;
for(int i=2;i<=m;i++)
{
while(top>1&&lcadep[i]<=depth[stk[top-1]])
add(stk[top-1],stk[top]),top--;
if(lcadep[i]<depth[stk[top]])
{//lca在top和top-1之间
depth[++n]=lcadep[i];//加入了非关键节点
add(n,stk[top]);
stk[top]=n;//lca入栈,now入栈
}
stk[++top]=i;
}
while(--top)
add(stk[top],stk[top+1]);
}
ll ans=0,dp[maxn],sum[maxn];
void dfs(int x,int fa)
{
dp[x]=0;
sum[x]=w[x];
ans+=w[x]*depth[x];//距离
for(auto &v:G[x])
if(v!=fa)
{
dfs(v,x);
sum[x]+=sum[v];
}
}
void dfs2(int x,int fa)
{
ans=min(ans,dp[x]);
for(auto &v:G[x])
{
if(v==fa)
continue;
dp[v]=dp[x]+(sum[1]-2*sum[v])*(depth[v]-depth[x]);
dfs2(v,x);
}
}
signed main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
const int lim=1e5+10005;
for(int i=2;i<=lim;i++)
if(!mindiv[i])//筛法记录最小因子
for(int j=i;j<=lim;j+=i)
if(!mindiv[j])
mindiv[j]=i;
//预处理完毕
while(cin>>m)
{
for(int i=1;i<=n;i++)
G[i].clear(),w[i]=bit.c[i]=bit.sum[i]=0;
for(int i=1;i<=m;i++)
cin>>w[i];
bit.n=m;
for(int i=2;i<=m;i++)
{//i!在原本树上的的深度
depth[i]=depth[i-1]+1;//(i-1)!基础上+多出来的质因子个数即为深度
int j=i;
for(;j!=mindiv[j];j/=mindiv[j])//直到j不为质数
depth[i]++;//处理i对于深度的贡献
//i!到1的深度处理好了
//处理完后j=maxdiv(i)
lcadep[i]=bit.query(m)-bit.query(j-1);//查询到j有多少个元素,因为这个树
for(j=i;j!=1;j/=mindiv[j])
bit.radd(mindiv[j],mindiv[j],1);
}
build();
ans=0;
dfs(1,1);
dp[1]=ans;
dfs2(1,1);
cout<<ans<<endl;
}
return 0;
}
/*
9
0 4 4 4 8 9 1 2 1
*/

F.Infinite String Comparision

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
void show(int flag,bool tra)
{
if((flag==1&&!tra)||(flag==2&&tra))
cout<<"<"<<endl;
else if((flag==2&&!tra)||(flag==1&&tra))
cout<<">"<<endl;
else
cout<<"="<<endl;
}
signed main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
string a,b;
while(cin>>a)
{
cin>>b;
int ok=0;//1小,2大,3等
bool tra=0;
int n=a.length(),m=b.length();
int lim=min(n,m);
if(n>m)
{
tra=1;
swap(n,m);
swap(a,b);
}
b+=b;
m<<=1;
for(int i=0;i<m;i++)
{
if(a[i%n]<b[i])
{
ok=1;
break;
}
else if(a[i%n]>b[i])
{
ok=2;
break;
}
}
show(ok,tra);
}
return 0;
}

H.Minimum-cost Flow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
ll gcd(ll a,ll b)
{
if(a<b)
swap(a,b);
ll r;
while((r=a%b)){
a=b;
b=r;
}
return b;
}
ll lcm(ll a,ll b)
{
return (a*b)/gcd(a,b);
}
map<int,int>mp;
struct MCMF
{
struct Edge
{
int from, to, cap, flow, cost;
Edge(int u, int v, int c, int f, int w)
: from(u), to(v), cap(c), flow(f), cost(w) {}
};
int n, m;
vector<Edge> edges;
vector<int> G[maxn];
int inq[maxn]; //是否在队列中
int d[maxn]; //bellmanford
int p[maxn]; //上一条弧
int a[maxn]; //可改进量
void init(int n)
{
this->n = n;
for (int i = 0; i <= n; i++)
G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap, int cost)
{
// edges.emplace_back(Edge(from, to, cap, 0, cost));
// edges.emplace_back(Edge(to, from, 0, 0, -cost));
edges.push_back(Edge(from, to, cap, 0, cost));
edges.push_back(Edge(to, from, 0, 0, -cost));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BellmanFord(int s, int t, int& flow, ll& cost)
{
for (int i = 0; i < n; i++)
d[i] = inf;
memset(inq, 0, sizeof(inq));
d[s] = 0;
inq[s] = 1;
p[s] = 0;
a[s] = inf;
queue<int> q;
q.push(s);
while (!q.empty())
{
int u = q.front();
q.pop();
inq[u] = 0;
for (int i = 0; i < G[u].size(); i++)
{
Edge& e = edges[G[u][i]];
if (e.cap > e.flow && d[e.to] > d[u] + e.cost)
{
d[e.to] = d[u] + e.cost;
p[e.to] = G[u][i];
a[e.to] = min(a[u], e.cap - e.flow);
if (!inq[e.to])
{
q.push(e.to);
inq[e.to] = 1;
}
}
}
}
// cout<<"DEBUG:BellmanFord\n";
// cout<<"flow"<<flow<<",cost="<<cost<<endl;
if (d[t] == inf)
return false; // 当没有可增广的路时退出
flow += a[t];
cost += (ll)d[t] * (ll)a[t];
mp[d[t]*a[t]]+=a[t];
for (int u = t; u != s; u = edges[p[u]].from)
{
edges[p[u]].flow += a[t];
edges[p[u] ^ 1].flow -= a[t];
}
return true;
}
int MincostMaxflow(int s, int t, ll& cost)
{
int flow = 0;
cost = 0;
while(BellmanFord(s, t, flow, cost));
return flow;
}
} mm;
int cost[maxn];
signed main()
{
// std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
ll n,m,u,v,w,c,q;
while(~scanf("%lld%lld",&n,&m))
{
mm.init(n+10);
mp.clear();
for(int i=1;i<=m;i++)
{
scanf("%lld%lld%lld",&u,&v,&cost[i]);
// cin>>u>>v>>cost[i];
mm.AddEdge(u,v,1,cost[i]);
}
ll tot=0,mf=mm.MincostMaxflow(1,n,tot);
scanf("%lld",&q);
// cin>>q;
while(q--)
{
scanf("%lld%lld",&u,&v);
// cin>>u>>v;
if(mf*u<v)
puts("NaN");
else{//容量乘以u,跑v的流量
ll now=0,x=0,y=v;
for(auto &i:mp)
{
// printf("%d,%d\n",i.first,i.second);
if(v>i.second*u)
{
v-=i.second*u;
x+=i.first*u;
}
else{
x+=i.first*v/i.second;
break;
}
}
ll g=gcd(x,y);
printf("%lld/%lld\n",x/g,y/g);
}
}
}
return 0;
}

I. 1 or 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
struct edge
{
int u,v;
edge(int u=0,int v=0):
u(u),v(v){}
};
struct Dinic
{
struct Edge
{
int from, to, cap, flow;
Edge(int u, int v, int c, int f):
from(u), to(v), cap(c), flow(f) {}
};
int n, m, s, t; //结点数,边数(包括反向弧),源点编号和汇点编号
vector<Edge> edges; //边表。edge[e]和edge[e^1]互为反向弧
vector<int> G[maxn]; //邻接表,G[i][j]表示节点i的第j条边在e数组中的序号
bool vis[maxn]; //BFS使用
int d[maxn]; //从起点到i的距离
int cur[maxn]; //当前弧下标
void init(int n)
{
this->n = n;
for (int i = 0; i < n; i++) G[i].clear();
edges.clear();
}
void AddEdge(int from, int to, int cap)
{
// edges.emplace_back(Edge(from, to, cap, 0));//魔改蔡队模板
// edges.emplace_back(Edge(to, from, 0, 0)); //反向弧
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
m = edges.size();
G[from].push_back(m - 2);
G[to].push_back(m - 1);
}
bool BFS()
{
memset(vis, 0, sizeof(vis));
memset(d, 0, sizeof(d));
queue<int> q;
q.push(s);
d[s] = 0;
vis[s] = 1;
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < G[x].size(); i++)
{
Edge& e = edges[G[x][i]];
if (!vis[e.to] && e.cap > e.flow)
{
vis[e.to] = 1;
d[e.to] = d[x] + 1;
q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x, int a)//x为当前点,a为当前边上流量
{
if (x == t || a == 0)//到达目标/流量为0
return a;
int flow = 0, f;
for (int& i = cur[x]; i < G[x].size(); i++)
{ //从上次考虑的弧
Edge& e = edges[G[x][i]];
if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0)
{
e.flow += f;
edges[G[x][i] ^ 1].flow -= f;
flow += f;
a -= f;
if (a == 0)
break;
}
}
if(!flow)//不知道可不可以加
d[x] = -1;//炸点优化必不可少,证明不必要的点下一次就不用遍历
return flow;
}
int Maxflow(int s, int t)
{
this->s = s, this->t = t;
int flow = 0;
while (BFS())
{
memset(cur, 0, sizeof(cur));
flow += DFS(s,inf);
}
return flow;
}
} di;
int d[maxn],deg[maxn];
signed main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
int n,m;
while(cin>>n>>m)
{
int s=2*n+1,t=2*n+2,tot=0;
for(int i=1;i<=n;i++)
cin>>d[i],tot+=d[i];
vector<edge>g(m);
di.init(2*n+2);
for(auto &e:g)
{
cin>>e.u>>e.v;
di.AddEdge(e.u,e.v+n,1);
di.AddEdge(e.v,e.u+n,1);
}
for(int i=1;i<=n;i++)
{
di.AddEdge(s,i,d[i]);
di.AddEdge(n+i,t,d[i]);
}
cout<<(di.Maxflow(s,t)==tot?"Yes":"No")<<endl;
}
return 0;
}

J.Easy Integration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
//#define int ll
const int maxn=2e6+10,inf=0x3f3f3f3f,mod=998244353;
ll fac[maxn],a[maxn];
ll quick(ll a,ll b){//快速幂
ll ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans%mod;
}
ll ccc(ll n,ll m){//求组合数
return (fac[n] * quick(fac[m], mod - 2) % mod * quick(fac[n - m], mod - 2) % mod)%mod;
}
void initccc()
{
fac[0] = 1;
for (int i = 1; i <maxn ;i++){
fac[i] = fac[i - 1] * i % mod;
}
}
signed main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#ifdef DEBUG
freopen("input.in", "r", stdin);
// freopen("output.out", "w", stdout);
#endif
ll n;
initccc();
while(cin>>n)
{
cout<<fac[n]*fac[n]%mod*quick(fac[2*n+1],mod-2)%mod<<endl;
// cout<<(quick(fac[2*n+1]*quick(fac[n]*fac[n]%mod,mod-2), mod - 2))%mod<<endl;
}
return 0;
}