贪心,分块 + 暴力,莫比乌斯反演,二分(单调栈)
# A. [USACO14OPEN]Fair Photography S
贪心就行了。
首先令 W 等于 1,S 等于 -1,观察到白牛可以染成黑牛(黑就黑吧)。
不难推出,如果前缀和 大于等于 0,那么从第一头牛到当前牛都可以选,当然还要判一下奇偶:
-
是偶数,那么可以选 .
-
是奇数,那么可以选 .
如果 小于 0 怎么办呢?
也很简单,直接开个 存一下 第一次出现位置,遇到 的情况,直接跟 里的值计算答案即可。
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
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 1e5 + 10;
int n;
struct node{
int pos, col;
inline bool operator < (const node &b) const{
return pos < b.pos;
}
}a[N];
int s[N], sum, ans;
map <int, int> vis;
signed main(){
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
n = read();
for(int i = 1; i <= n; ++i){
int x = read(); char c; scanf("%c", &c);
a[i] = (node){x, c == 'W' ? 1 : -1};
}
sort(a + 1, a + 1 + n);
a[n + 1].pos = 1e9;
vis[0] = 1;
for(int i = 1; i <= n; ++i){
sum = sum + a[i].col;
if(sum < 0 && !vis[sum]) vis[sum] = i + 1;
if(sum >= 0){
if(sum & 1) ans = max(ans, a[i].pos - a[2].pos);
else ans = max(ans, a[i].pos - a[1].pos);
}else ans = max(ans, a[i].pos - a[vis[sum]].pos);
}
write(ans), puts("");
return 0;
}
# B. DZY Loves Colors
分块水题。
维护一个 标记,标记整块是否被某一种颜色覆盖。
注意到一次修改最多会使两个块不再被同一种颜色覆盖,是 级别的,所以直接暴力枚举整块修改即可。
写的时候注意 标记的下放,不要忘了放,也不要放多了(
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
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 1e5 + 10;
int n, m, B, tot;
int be[N], L[N], R[N];
int c[N], w[N], sum[N], tag[N], tagc[N];
inline void build(){
tot = n / B;
if(n % B) tot++;
for(int i = 1; i <= n; ++i) be[i] = (i - 1) / B + 1;
for(int i = 1; i <= tot; ++i) L[i] = R[i - 1] + 1, R[i] = i * B;
R[tot] = n;
for(int i = 1; i <= n; ++i) c[i] = i;
}
inline void upd(int l, int r, int k){
for(int i = l; i <= r; ++i){
if(!tagc[be[l]]) w[i] += abs(c[i] - k), sum[be[l]] += abs(c[i] - k), c[i] = k;
else w[i] += abs(tagc[be[l]] - k), sum[be[l]] += abs(tagc[be[l]] - k), c[i] = k;
}
if(tagc[be[l]]){
for(int i = L[be[l]]; i < l; ++i) c[i] = tagc[be[l]];
for(int i = r + 1; i <= R[be[r]]; ++i) c[i] = tagc[be[r]];
}
tagc[be[l]] = 0;
}
inline void update(int l, int r, int k){
if(be[l] == be[r]) return upd(l, r, k);
for(int i = be[l] + 1; i <= be[r] - 1; ++i){
if(tagc[i]) sum[i] += B * abs(k - tagc[i]), tag[i] += abs(k - tagc[i]);
else for(int j = L[i]; j <= R[i]; ++j) w[j] += abs(c[j] - k), sum[i] += abs(c[j] - k), c[j] = k;
tagc[i] = k;
}
upd(l, R[be[l]], k), upd(L[be[r]], r, k);
}
inline int query(int l, int r){
int res = 0;
if(be[l] == be[r]){
for(int i = l; i <= r; ++i) res += w[i] + tag[be[l]];
return res;
}
for(int i = be[l] + 1; i <= be[r] - 1; ++i) res += sum[i];
for(int i = l; i <= R[be[l]]; ++i) res += w[i] + tag[be[l]];
for(int i = L[be[r]]; i <= r; ++i) res += w[i] + tag[be[r]];
return res;
}
signed main(){
freopen("test.in", "r", stdin);
freopen("B.out", "w", stdout);
double st = clock();
n = read(), m = read(), B = sqrt(n);
build();
while(m--){
int op = read(), l = read(), r = read();
if(op == 1) update(l, r, read());
else write(query(l, r)), puts("");
}
double ed = clock();
cerr << ed - st << endl;
return 0;
}
# C. [USACO12MAR]Large Banner G
一开始没什么思路,后来发现一条线段是可以平移旋转的,所以只统计从 开始的线段就行了。
枚举从 开始的线段终点是 。
众所周知,中间不经过其他整点的条件相当于是 。
再来看能平移到那些位置,不难发现有 个位置,平且可以旋转,所以再乘个 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
using namespace std;
namespace IO{
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return x * f;
}
template <typename T> inline void write(T x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace IO;
const int N = 1e5 + 10;
int n, m, l, h, mod, ans;
int mu[N], p[N], vis[N], tot;
inline void euler(int n){
mu[1] = 1;
for(int i = 2; i <= n; ++i){
if(!vis[i]) p[++tot] = i, mu[i] = -1;
for(int j = 1; j <= tot && i * p[j] <= n; ++j){
vis[i * p[j]] = 1;
if(i % p[j]) mu[i * p[j]] = -mu[i];
else break;
}
}
}
inline int add(int x) {return x >= mod ? x - mod : x;}
inline int sub(int x) {return x < 0 ? x + mod : x;}
inline int mul(int x, int y) {return 1ll * x * y % mod;}
inline int cal(int x) {return (x % mod + mod) % mod;}
inline int sqr(int x) {return x * x;}
signed main(){
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
n = read(), m = read(), l = read(), h = read(), mod = read();
if(n > m) swap(n, m);
euler(n);
if(l <= 1) ans = add(mul(n, m + 1) + mul(m, n + 1));
for(int d = 1; d <= n; ++d){
int res = 0;
for(int i = 1; i <= n / d; ++i){
if(sqr(h) <= sqr(i * d)) continue;
int L = ceil(1.0 * sqrt(sqr(l) - sqr(i * d)) / d), R = min(m, (int)floor(sqrt(sqr(h) - sqr(i * d)))) / d;
if(sqr(l) <= sqr(i * d)) L = 1;
if(L > R || L > m) continue;
res = add(res + mul(n - i * d + 1, mul(add(m - L * d + 1 + m - R * d + 1), R - L + 1)));
}
ans = add(ans + cal(mu[d] * res));
}
write(ans), puts("");
return 0;
}
# D. 牛宫
考场上写个 发现暴力跑的飞快,结果 不给开 /fn/fn/fn
大概是套路的枚举行的上下边界,再枚举计算一下行的前缀和,然后二分列的长度 , 一下就行。
我没写正解,只有暴力代码,就不放了吧(