2020杭电多校(第一场)

比赛链接

收获

  • 对图可以进行分块:按照度数将点分为“大点”和“小点”——大点是度数 $>\sqrt n$ 的点,不超过 $\sqrt n$ 个;小点是度数 $\leqslant \sqrt n$ 的点。

1004 Distinct Sub-palindromes

  • 当 $n=1$ 时,答案是 $26$,即所有字母;
  • 当 $n=2$ 时,答案是 $26\times26$,即所有字母任意组合,不同回文子串数为 $2$;
  • 当 $n=3$ 时,答案是 $26\times26\times26$,即所有字母任意组合,不同回文子串数为 $3$;
  • 当 $n\geqslant4$ 时,答案是 $A_{26}^3=26\times25\times24$。首先考虑 $n=4$ 的情况,此时,我们可以用三个字母构造出不同回文子串数为 $3$ 的序列:$abca$,当固定了前三个位置填 $a,b,c$ 后,第四个位置只能填 $a$,因此是 $A_{26}^3$ 种;考虑 $n=5$,会发现在 $abca$ 的基础上只能填 $b$ 才能仍然保持不同回文子串数为 $3$;同理可推得,要使不同回文子串数保持为 $3$,只能 $abcabcabc\cdots$,因此 $n\geqslant4$ 时答案都是 $A_{26}^3$。
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

int T, n;

int main(){
for(read(T); T; T--){
read(n);
if(n == 1) puts("26");
else if(n == 2) puts("676");
else if(n == 3) printf("%d\n", 26*26*26);
else printf("%d\n", 26*25*24);
}
return 0;
}

1005 Fibonacci Sum

$\textbf{Fibonacci}$ 数列通项公式:

$\sqrt 5$ 在模意义下就是 $x^2\equiv 5\pmod p$ 的解,二次剩余解之得到:$x=383008016$. 记 $A=\frac{1+\sqrt5}{2},\,B=\frac{1-\sqrt 5}{2},\,D=\frac{1}{\sqrt 5}$,那么 $F_n=D(A^n-B^n)$.

喜闻乐见的推式子时间:

枚举 $j$ 即可。

不过此题卡常,可以有两个常数优化:

  • 快速幂的时候指数降幂;
  • 从 $E_j$ 到 $E_{j+1}$ 可以 $O(1)$ 递推,不需要 $O(\lg 10^9)$ 重新计算。
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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

const LL MOD = 1e9+9;
const LL _5 = 383008016;
const LL inv2 = 500000005;
const LL A = 691504013;
const LL B = 308495997;
const LL D = 276601605;

inline LL fpow(LL bs, LL idx){
bs %= MOD, idx %= (MOD - 1);
LL res = 1;
while(idx){
if(idx & 1) (res *= bs) %= MOD;
(bs *= bs) %= MOD;
idx >>= 1;
}
return res;
}

LL N, C; int K;
LL fact[100005] = {1}, invfact[100005] = {1};

int main(){
for(int i = 1; i <= 100000; i++){
fact[i] = fact[i-1] * i % MOD;
invfact[i] = fpow(fact[i], MOD-2);
}
int T; for(read(T); T; T--){
read(N, C, K);
LL ans = 0;
LL Ac = fpow(A, C), Bc = fpow(B, C), Ec = Ac * fpow(Bc, MOD-2) % MOD;
LL E = fpow(Bc, K);
for(int j = 0; j <= K; j++){
LL binom = fact[K] * invfact[j] % MOD * invfact[K-j] % MOD;
if((K - j) & 1) binom = MOD - binom;
if(E == 1) ans += (N + 1) % MOD * binom % MOD;
else ans += binom * (fpow(E, N+1) - 1) % MOD * fpow(E - 1, MOD-2) % MOD;
ans = ((ans % MOD) + MOD) % MOD;
E = E * Ec % MOD;
}
ans = ans * fpow(D, K) % MOD;
printf("%lld\n", ans);
}
return 0;
}

1006 Finding a MEX

按照度数将点分为“大点”和“小点”——大点是度数 $>\sqrt n$ 的点,不超过 $\sqrt n$ 个;小点是度数 $\leqslant \sqrt n$ 的点。对于小点,每次询问直接暴力统计,复杂度 $O(\sqrt n)$;对于大点,我们维护一个值域树状数组,存储与它相邻的点的值,那么求 $\text{MEX}$ 可以在树状数组上二分。考虑如何维护:每次更改一个点的值时,就更新与之相邻的大点的树状数组,由于大点不超过 $\sqrt n$ 个,所以一次更改操作也是 $O(\sqrt n)$ 的,这样复杂度就得到了保证。

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
#include<bits/stdc++.h>

using namespace std;

template<typename T>void read(T&x){x=0;int fl=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')
fl=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}x*=fl;}
template<typename T,typename...Args>inline void read(T&t,Args&...args){read(t);read(args...);}

typedef long long LL;
typedef vector<int> vi;
typedef pair<int, int> pii;
#define mp(x, y) make_pair(x, y)
#define pb(x) emplace_back(x)

inline int lowbit(int x){ return x & -x; }
inline void add(vi &c, vi&b, int n, int x, int val){
if(x > n) return;
b[x] += val;
if(val == 1 && b[x] == 1){
while(x <= n){
c[x] += val;
x += lowbit(x);
}
}
else if(val == -1 && b[x] == 0){
while(x <= n){
c[x] += val;
x += lowbit(x);
}
}
}
inline int sum(vi&c, int n, int x){
int res = 0;
while(x){
res += c[x];
x -= lowbit(x);
}
return res;
}
inline int mex(vi&c, int n){
int l = 1, r = n;
while(l < r){
int mid = (l + r) >> 1;
if(sum(c, n, mid) < mid) r = mid;
else l = mid + 1;
}
return l;
}

const int N = 100005;

int T, n, m, a[N];

int main(){
for(read(T); T; T--){
read(n, m);
int sq = sqrt(n);
vector<vi> edge(n+1, vi()), adjBig(n+1, vi());
vi deg(n+1, 0);
for(int i = 1; i <= n; i++) read(a[i]), a[i]++;
for(int i = 1; i <= m; i++){
int u, v; read(u, v);
edge[u].pb(v), edge[v].pb(u);
deg[u]++, deg[v]++;
}
vector<vi> c(n+1, vi()), b(n+1, vi());
for(int i = 1; i <= n; i++){
if(deg[i] > sq){
c[i].resize(deg[i]+2);
b[i].resize(deg[i]+2);
for(auto &to : edge[i]) adjBig[to].pb(i);
}
}
for(int i = 1; i <= n; i++)
for(auto &k : adjBig[i])
add(c[k], b[k], deg[k]+1, a[i], 1);
int q; for(read(q); q; q--){
int opt, u; read(opt, u);
if(opt == 1){
for(auto &k : adjBig[u]) add(c[k], b[k], deg[k]+1, a[u], -1);
int x; read(x); x++;
a[u] = x;
for(auto &k : adjBig[u]) add(c[k], b[k], deg[k]+1, x, 1);
}
else{
if(deg[u] > sq) printf("%d\n", mex(c[u], deg[u]+1) - 1);
else{
vi r(deg[u]+1);
for(auto &k : edge[u])
if(a[k] <= deg[u] + 1)
r[a[k]]++;
int res = 0;
for(res = 1; res <= deg[u] + 1; res++){
if(r[res] == 0){
printf("%d\n", res - 1);
break;
}
}
}
}
}
}
return 0;
}

1009 Leading Robots

首先,那些 $a,p$ 均比某个机器人小的机器人可以扔掉不管了。然后,由于超过别人的机器人一定是 $a$ 更大的,所以我们可以按照 $a$ 升序排序,又已经剔掉了 $a,p$ 均比别人小的机器人,所以现在 $p$ 是降序的。考虑 $t=0$ 时领先的那个机器人(排序后的第一个元素),我们只需要往后找到谁第一个超过它,那么谁就领先,随后再往后找谁第一个超过现在领先的机器人……如此解决问题。

设排序后的机器人序列为:$\{x_1,x_2,\cdots,x_n\}$,则机器人 $x_3$ 先于 $x_2$ 超过 $x_1$ 的充要条件是:$1,3$ 相遇的时间小于 $1,2$ 相遇的时间,即 $\sqrt{\frac{2(p_1-p_3)}{a_3-a_1}}<\sqrt{\frac{2(p_1-p_2)}{a_2-a_1}}$,即 $\frac{p_1-p_3}{a_3-a_1}<\frac{p_1-p_2}{a_2-a_1}$,乘出来后用行列式表达就是:$\begin{vmatrix}1&1&1\\a_1&a_2&a_3\\p_1&p_2&p_3\end{vmatrix}>0$。注意到,假设点 $X_1(a_1,p_1),\,X_2(a_2,p_2),\,X_3(a_3,p_3)$,那么上述行列式就是 $\Delta X_1X_2X_3$ 的有向面积。换句话说,$x_3$ 比 $x_2$ 先超过 $x_1$ 的充要条件是 $\overrightarrow{X_1X_3}$ 在 $\overrightarrow{X_1X_2}$ 的左侧。那么,我们把这些点 $X_i(a_i,p_i)$ 画出来就会发现,答案其实就是求一个凸包(当然处理一下两个机器人重合的特殊情况)。

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
#include<algorithm>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>

using namespace std;

typedef long long LL;

const LL INF = 1e14;
const int N = 50100;

//------------------------------------ Vector & Point ------------------------------------//
struct Vector{
LL x, y;
int id;
Vector() {}
Vector(LL x, LL y):x(x), y(y){ id = -1; }
};
typedef Vector Point;
Vector operator + (Vector A, Vector B){ return Vector(A.x + B.x, A.y + B.y); }
Vector operator - (Vector A, Vector B){ return Vector(A.x - B.x, A.y - B.y); }
Vector operator * (LL k, Vector A){ return Vector(k * A.x, k * A.y); }
bool operator < (const Vector &A, const Vector &B){
return A.x == B.x ? A.y < B.y : A.x < B.x;
}
bool operator == (const Vector &A, const Vector &B){ return A.x == B.x && A.y == B.y; }
// dot product
LL operator * (Vector A, Vector B){ return A.x * B.x + A.y * B.y; }
// cross product
LL operator ^ (Vector A, Vector B){ return A.x * B.y - A.y * B.x; }

//------------------------------------------------------------------------------//

//------------------------------------ Convex Hull ------------------------------------//
void ConvexHull(int n, Point p[], Point sta[], int &staid){
// there're n points stored in p[], the points on convex hull will be saved in sta[]
sort(p+1, p+n+1);
n = unique(p+1, p+n+1) - (p+1);
staid = 0;
for(int i = 1; i <= n; i++){
// points on edge
// while(staid > 1 && ((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) < 0) staid--;
// no points on edge
while(staid > 1 && ((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) <= 0) staid--;
sta[++staid] = p[i];
}
int k = staid;
for(int i = n-1; i >= 1; i--){
// points on edge
// while(staid > k && ((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) < 0) staid--;
// no points on edge
while(staid > k && ((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) <= 0) staid--;
sta[++staid] = p[i];
}
if(n > 1) staid--;
}

int T, n;
Point p[N], ch[N];

int main(){
for(scanf("%d", &T); T; T--){
scanf("%d", &n);
for(int i = 0; i <= n + 5; i++)
p[i].x = p[i].y = 0, p[i].id = -1;

LL mxy = -INF, mxx = -INF;
for(int i = 1; i <= n; i++){
scanf("%lld%lld", &p[i].y, &p[i].x);
p[i].id = i;
mxy = max(mxy, p[i].y);
mxx = max(mxx, p[i].x);
}
p[++n] = Point(0, 0);
p[++n] = Point(0, mxy);
p[++n] = Point(mxx, 0);
sort(p+1, p+n+1);
for(int i = 2; i <= n; i++)
if(p[i] == p[i-1])
p[i].id = p[i-1].id = -1;
int tot = 0;
ConvexHull(n, p, ch, tot);

int ans = 0;
for(int i = 1; i <= tot; i++)
if(ch[i].id != -1)
ans++;
printf("%d\n", ans);
}
return 0;
}

1012 Mow

这就是一个半平面交的经典题啊,类似 poj3384

如果 $B\geqslant A$,就全部手动打扫;否则,就用机器打扫它能打扫的所有位置,即圆在这个多边形内部能覆盖的最大面积,然后手动打扫剩下的位置。把所有边往里面移动 $r$,半平面交得到圆心可以到达的区域,那么圆能覆盖的面积就是这个区域面积加上该区域各条边支出去的小矩形面积再加上一个整圆的面积。

只是这道题估计造了些 corner cases 的数据,尽管它保证了逆时针或顺时针输入一个凸多边形,但是直接用总会 WA。后来干脆先求凸包再做就 AC 了。

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
160
161
162
163
164
165
166
167
168
169
170
171
#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

const double eps = 1e-14;
const double PI = 4 * atan2(1, 1);
const double INF = 1e16;
const int N = 1005;
inline int sgn(double x){
if(fabs(x) < eps) return 0;
else if(x > 0) return 1;
else return -1;
}
inline int cmp(double x, double y){ return sgn(x-y); }

//------------------------------------ Vector & Point ------------------------------------//
struct Vector{
double x, y;
Vector() {}
Vector(double x, double y):x(x), y(y){}
void read(){ scanf("%lf%lf", &x, &y); }
};
typedef Vector Point;
Vector operator + (Vector A, Vector B){ return Vector(A.x + B.x, A.y + B.y); }
Vector operator - (Vector A, Vector B){ return Vector(A.x - B.x, A.y - B.y); }
Vector operator * (double k, Vector A){ return Vector(k * A.x, k * A.y); }
Vector operator * (Vector A, double k){ return k * A; }
bool operator < (const Vector &A, const Vector &B){
return cmp(A.x, B.x) == 0 ? cmp(A.y, B.y) < 0 : cmp(A.x, B.x) < 0;
}
bool operator == (const Vector &A, const Vector &B){ return (cmp(A.x, B.x) == 0) && (cmp(A.y, B.y) == 0); }
// dot product
double operator * (Vector A, Vector B){ return A.x * B.x + A.y * B.y; }
// cross product
double operator ^ (Vector A, Vector B){ return A.x * B.y - A.y * B.x; }
double Length(Vector A){ return sqrt(A * A); }
// get the normal vector of A
Vector Normal(Vector A){ double L = Length(A); return Vector(-A.y/L, A.x/L); }
//------------------------------------------------------------------------------//

//------------------------------------ Line ------------------------------------//
struct Line{
Point p;
Vector v;
double ang; // angle of inclination (-PI, PI]
Line() {}
Line(Point p, Vector v):p(p), v(v){ ang = atan2(v.y, v.x); }
Line(double a, double b, double c){ // ax + by + c = 0
if(sgn(a) == 0) p = Point(0, -c/b), v = Vector(1, 0);
else if(sgn(b) == 0) p = Point(-c/a, 0), v = Vector(0, 1);
else p = Point(0, -c/b), v = Vector(-b, a);
}
Point getPoint(double t){ return p + v * t; }
bool operator < (const Line &L) const{ return ang < L.ang; }
};
bool PointOnRight(Point p, Line l){ return sgn(l.v ^ (p-l.p)) < 0; }
Point GetLineIntersection(Line l1, Line l2){
Vector u = l1.p - l2.p;
double t = (l2.v ^ u) / (l1.v ^ l2.v);
return l1.p + l1.v * t;
}

double PolygonArea(int n, Point p[]){
double S = 0;
for(int i = 2; i < n; i++)
S += ((p[i] - p[1]) ^ (p[i+1] - p[1])) / 2;
return S;
}

void ConvexHull(int n, Point p[], Point sta[], int &staid){
// there're n points stored in p[], the points on convex hull will be saved in sta[]
sort(p+1, p+n+1);
n = unique(p+1, p+n+1) - (p+1);
staid = 0;
for(int i = 1; i <= n; i++){
// points on edge
// while(staid > 1 && sgn((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) < 0) staid--;
// no points on edge
while(staid > 1 && sgn((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) <= 0) staid--;
sta[++staid] = p[i];
}
int k = staid;
for(int i = n-1; i >= 1; i--){
// points on edge
// while(staid > k && sgn((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) < 0) staid--;
// no points on edge
while(staid > k && sgn((sta[staid]-sta[staid-1]) ^ (p[i]-sta[staid-1])) <= 0) staid--;
sta[++staid] = p[i];
}
if(n > 1) staid--;
}

//------------------------------------ HalfplaneIntersection ------------------------------------//
struct Halfplane{
Point P[N]; // P[i] is the intersection of line Q[i] and Q[i+1]
Line Q[N]; // deque
void HalfplaneIntersection(Line L[], int n, Point res[], int &m){
// L[] are the lines, n is the number of lines, res[] stores the result of the intersection (a polygon)
// m is the number of points of the intersection (which is a polygon)
sort(L + 1, L + n + 1);
int head, tail;
Q[head = tail = 0] = L[1];
for(int i = 2; i <= n; i++){
while(head < tail && PointOnRight(P[tail - 1], L[i])) tail--;
while(head < tail && PointOnRight(P[head], L[i])) head++;
Q[++tail] = L[i];
if(sgn(Q[tail].v ^ Q[tail - 1].v) == 0){ // parallel, the inner one remains
tail--;
if(!PointOnRight(L[i].p, Q[tail])) Q[tail] = L[i];
}
if(head < tail) P[tail - 1] = GetLineIntersection(Q[tail-1], Q[tail]);
}
while(head < tail && PointOnRight(P[tail - 1], Q[head])) tail--; // delete useless plane
P[tail] = GetLineIntersection(Q[tail], Q[head]);

m = 0;
for(int i = head; i <= tail; i++) res[++m] = P[i];
}
};
//-----------------------------------------------------------------------------------------------//

int T, n;
LL A, B;
double r;

Point p[N], q[N], ch[N];
Line l[N];
Halfplane hp;

int main(){
for(scanf("%d", &T); T; T--){
scanf("%d%lf", &n, &r);
scanf("%lld%lld", &A, &B);

for(int i = 1; i <= n; i++) p[i].read();

int tot = 0;
ConvexHull(n, p, ch, tot);
if(tot <= 2){ printf("%.12f\n", 0.0); continue; }

double Stot = PolygonArea(tot, ch);
if(B >= A){
printf("%.12f\n", Stot * A);
continue;
}

int qn = 0;
ch[0] = ch[n];
for(int i = 1; i <= n; i++){
Point t = ch[i] + r * Normal(ch[i] - ch[i-1]);
l[i] = Line(t, ch[i] - ch[i-1]);
}
hp.HalfplaneIntersection(l, n, q, qn);

if(qn < 3){
printf("%.12f\n", Stot * A);
continue;
}

double SB = PolygonArea(qn, q);
q[0] = q[qn];
for(int i = 1; i <= qn; i++)
SB += Length(q[i] - q[i-1]) * r;
SB += PI * r * r;

printf("%.12f\n", (Stot - SB) * A + SB * B);
}
return 0;
}
作者

xyfJASON

发布于

2020-07-21

更新于

2021-08-28

许可协议

评论