USACO 2017 Dec Solution更好的阅读体验戳此进入题面 Luogu 链接LG-P4122 [USACO17DEC]Blocked Billboard B题面ExamplesSolutionCodeLG-P4089 [USACO17DEC]The Bovine Shuffle S题面ExamplesSolutionCodeLG-P4087 [USACO17DEC]Milk Measurement S题面ExamplesSolutionCodeLG-P4086 [USACO17DEC]My Cow Ate My Homework S题面ExamplesSolutionCodeLG-P4083 [USACO17DEC]A Pie for a Pie G题面ExamplesSolutionCodeLG-P4084 [USACO17DEC]Barn Painting G题面ExamplesSolutionCodeLG-P4085 [USACO17DEC]Haybale Feast G题面ExamplesSolutionCodeLG-P4090 [USACO17DEC]Greedy Gift Takers P题面ExamplesSolutionCodeLG-P4081 [USACO17DEC]Standing Out from the Herd P题面LG-P4082 [USACO17DEC]Push a Box P题面ExamplesSolutionCodeUPD
给定不交的两个矩形的左下和右上坐标,再给定第三个矩形的左下右上坐标,求两个矩形的没有被第三个矩形所覆盖的面积。
坐标均在
Input_1
311 2 3 5
26 0 10 4
32 1 8 3
Output_1
1117
水题,但是也可以想一下如何更好实现,可以直接枚举范围内所有点的坐标然后判断,这样会比嗯讨论点之间的位置方便得多,注意判断
531
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26int v[4][5];
27int ans(0);
28int main(){
29 for(int i = 1; i <= 3; ++i)for(int j = 1; j <= 4; ++j)v[i][j] = read();
30
31 for(int i = -1000; i <= 1000; ++i)
32 for(int j = -1000; j <= 1000; ++j)
33 if((JGx(1, i, j) || JGx(2, i, j)) && !JGx(3, i, j))++ans;
34 printf("%d\n", ans);
35 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
36 return 0;
37}
38
39template < typename T >
40inline T read(void){
41 T ret(0);
42 short flag(1);
43 char c = getchar();
44 while(c != '-' && !isdigit(c))c = getchar();
45 if(c == '-')flag = -1, c = getchar();
46 while(isdigit(c)){
47 ret *= 10;
48 ret += int(c - '0');
49 c = getchar();
50 }
51 ret *= flag;
52 return ret;
53}
给定
Input_1
214
23 2 1 3
Output_1
113
一道水题,画画图就不难想到这玩意实际上是内向树再加一条外向边,可以认为其为一个奇怪的有向的基环树森林,然后我们要求的就是每个环长求和。这个不难理解,我们发现内向树上所有节点最终都会汇集到根节点上,然后根节点出去的外向边,也就是形成的这个环路,每个点一定一直都有犇,所以
至于实现拓扑排序可能比较短,或者 dfs 也行,所以我写的并查集。
691
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26int N;
27int cnt(0);
28int to[110000];
29bool vis[110000];
30class UnionFind{
31private:
32 int fa[110000];
33 int dis[110000];
34public:
35 UnionFind(void){for(int i = 1; i <= 101000; ++i)fa[i] = i, dis[i] = 0;}
36 int Find(int x){return x == fa[x] ? x : fa[x] = Find(fa[x]);}
37 int GetDis(int x){return dis[x];};
38 void Union(int origin, int son){fa[son] = Find(origin);}
39}uf;
40int main(){
41 N = read();
42 for(int i = 1; i <= N; ++i){
43 to[i] = read();
44 if(uf.Find(to[i]) == i){
45 int cur(to[i]);
46 while(cur != i)++cnt, cur = to[cur];
47 ++cnt;
48 }else uf.Union(to[i], i);
49 }
50 printf("%d\n", cnt);
51 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
52 return 0;
53}
54
55template < typename T >
56inline T read(void){
57 T ret(0);
58 short flag(1);
59 char c = getchar();
60 while(c != '-' && !isdigit(c))c = getchar();
61 if(c == '-')flag = -1, c = getchar();
62 while(isdigit(c)){
63 ret *= 10;
64 ret += int(c - '0');
65 c = getchar();
66 }
67 ret *= flag;
68 return ret;
69}
llq 有一大群 OIer,每个 OIer 都有相同的初始日做题量
Input_1
514 10
27 3 +3
34 2 -1
49 3 -1
51 1 +2
Output_1
113
离散化不难想到吧,然后按照日期排序不难想到吧。
这样之后就更不难想到线段树维护了,单点修改,区间(整个区间)查询,比一般线段树都好写,也不用 lazytag。
不过我不想写线段树线段树看着不好看,所以考虑无脑模拟一下,离散化后,排序之后,开个 vector
插入所有的 OIer 的做题量(也就是原题犇的产奶量),然后额外开个数组对应着记录每个 OIer 的做题量,每次修改就是在静态数组里找对应做题量然后从 vector
中删除后修改值再插入回去,中间判断一下最优值或其的数量是否有变化,有变化就 ++cnt
即可。
然后发现因为 vector
的 erase
和 insert
之类的都是 vector
变成 multiset
,这样最终复杂度
然后注意我们离散化之后应该还有大量的没有修改过的 OIer(犇),他们始终保持着原来的量,所以我们还需要额外加一个数值为原来的初始值并不改变即可。
xxxxxxxxxx
781
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26int N, G;
27ll cur[110000];
28struct Node{
29 int date, num, delt;
30 friend const bool operator < (const Node &x, const Node &y){return x.date < y.date;}
31};
32vector < Node > notes;
33multiset < ll > milk;
34vector < int > vals;
35int main(){
36 N = read(), G = read();
37 for(int i = 1; i <= N; ++i){
38 int d = read(), n = read(), del = read();
39 if(del == 0){--N, --i; continue;}
40 notes.emplace_back(Node{d, n, del});
41 milk.insert(G);
42 cur[i] = G;
43 vals.emplace_back(n);
44 }sort(notes.begin(), notes.end());
45 sort(vals.begin(), vals.end());
46 milk.insert(G);
47 int cnt(0);
48 for(auto &x : notes){
49 x.num = distance(vals.begin(), upper_bound(vals.begin(), vals.end(), x.num));
50 bool mvbst(false), insbst(false), mulbst(false);
51 if(cur[x.num] == *prev(milk.end()))mvbst = true;
52 if((int)milk.size() >= 2 && *prev(milk.end()) == *prev(milk.end(), 2))mulbst = true;
53 milk.erase(milk.lower_bound(cur[x.num]));
54 cur[x.num] += x.delt;
55 if(cur[x.num] >= *prev(milk.end()))insbst = true;
56 milk.insert(milk.upper_bound(cur[x.num]), cur[x.num]);
57 if((int)milk.size() >= 2 && *prev(milk.end()) == *prev(milk.end(), 2))mulbst = true;
58 if((mvbst && !insbst) || (!mvbst && insbst) || (mvbst && insbst && mulbst))++cnt;
59 }printf("%d\n", cnt);
60 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
61 return 0;
62}
63
64template < typename T >
65inline T read(void){
66 T ret(0);
67 short flag(1);
68 char c = getchar();
69 while(c != '-' && !isdigit(c))c = getchar();
70 if(c == '-')flag = -1, c = getchar();
71 while(isdigit(c)){
72 ret *= 10;
73 ret += int(c - '0');
74 c = getchar();
75 }
76 ret *= flag;
77 return ret;
78}
这题你们读完应该就切掉了吧。。
给定长度为
Input_1
xxxxxxxxxx
215
23 1 9 2 7
Output_1
xxxxxxxxxx
112
随便做一下后缀和和后缀最小值,然后
xxxxxxxxxx
641
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23
24
25template< typename T = int >
26inline T read(void);
27
28int N;
29int a[110000];
30int sum[110000];
31int smin[110000];
32
33int main(){
34 N = read();
35 for(int i = 1; i <= N; ++i)a[i] = read();
36 smin[N + 1] = INT_MAX;
37 for(int i = N; i >= 1; --i)
38 sum[i] = sum[i + 1] + a[i], smin[i] = min(smin[i + 1], a[i]);
39 double mx(-1.0);
40 vector < int > mxk;
41 for(int k = 1; k <= N - 2; ++k){
42 double val = double(sum[k + 1] - smin[k + 1]) / double(N - k - 1);
43 if(abs(val - mx) <= EPS)mxk.emplace_back(k);
44 else if(val > mx){mx = val, mxk.clear(), mxk.emplace_back(k);}
45 }for(auto K : mxk)printf("%d\n", K);
46 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
47 return 0;
48}
49
50template < typename T >
51inline T read(void){
52 T ret(0);
53 short flag(1);
54 char c = getchar();
55 while(c != '-' && !isdigit(c))c = getchar();
56 if(c == '-')flag = -1, c = getchar();
57 while(isdigit(c)){
58 ret *= 10;
59 ret += int(c - '0');
60 c = getchar();
61 }
62 ret *= flag;
63 return ret;
64}
sssmzy 和 zpair 各自烤了 -1
。
馅饼不能被重复赠送。
Tips:输入评分时前
Input_1
xxxxxxxxxx
512 1
21 1
35 0
44 2
51 4
Output_1
xxxxxxxxxx
213
21
一道神仙线段树优化建图,个人认为是一道很有意思的题,缝合怪,码量大细节多。
首先建图应该很好想到吧?日常正难则反,以 sssmzy 的 zpair 评分为
不难想到我们可以把 sssmzy 的和 zpair 的分别排个序,不难想到前者的馅饼按照后者的评分排,后者的馅饼按照前者的评分排。这样每次连边一定是连到了对方馅饼的某一段区间,不难想到为
线段树上每个点向其子节点连个
连完之后从每个可行的
xxxxxxxxxx
1231
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23
24
25
26
27
28template< typename T = int >
29inline T read(void);
30
31struct Edge{
32 Edge* nxt;
33 int to;
34 bool val;
35 OPNEW;
36}ed[MAXN8 << 1];
37ROPNEW(ed);
38Edge* head[MAXN8];
39
40struct Node{int a, b; int idx;};
41
42int leaf[MAXN2];
43int N, D;
44
45Node val[MAXN2];
46Node A[MAXN], B[MAXN];
47int dis[MAXN8];
48int ans[MAXN4];
49
50class SegTree{
51
52
53
54private:
55public:
56 void Build(int p = 1, int gl = 1, int gr = N2){
57 if(gl == gr)return leaf[gl = gr] = p, void();
58 Build(LS, gl, MID), Build(RS, MID + 1, gr);
59 head[p] = new Edge{head[p], LS, 0};
60 head[p] = new Edge{head[p], RS, 0};
61 }
62 void Connect(int ori, int l, int r, int p = 1, int gl = 1, int gr = N2){
63 if(l > r)return;
64 if(l <= gl && gr <= r)return head[ori] = new Edge{head[ori], p, 1}, void();
65 if(l <= MID)Connect(ori, l, r, LS, gl, MID);
66 if(MID + 1 <= r)Connect(ori, l, r, RS, MID + 1, gr);
67 }
68}st;
69void bfs(void){
70 memset(dis, 0x3f, sizeof dis);
71 deque < int > dq;
72 for(int i = 1; i <= N2; ++i)
73 if((!val[i].a && i >= N + 1) || (!val[i].b && i <= N))
74 dis[leaf[i]] = 1, dq.push_back(leaf[i]);
75 while(!dq.empty()){
76 int tp = dq.front(); dq.pop_front();
77 for(auto i = head[tp]; i; i = i->nxt)
78 if(dis[tp] + i->val < dis[SON]){
79 dis[SON] = dis[tp] + i->val;
80 i->val ? dq.push_back(SON) : dq.push_front(SON);
81 }
82 }
83 for(int i = 1; i <= N2; ++i)ans[val[i].idx] = dis[leaf[i]];
84}
85int main(){
86 // freopen("P4083_5.in", "r", stdin);
87
88
89 N = read(), D = read(); st.Build();
90 for(int i = 1; i <= N; ++i)A[i].a = read(), A[i].b = read(), A[i].idx = i;
91 for(int i = 1; i <= N; ++i)B[i].a = read(), B[i].b = read(), B[i].idx = i + N;
92 sort(A + 1, A + N + 1, CMP_SECOND);
93 sort(B + 1, B + N + 1, CMP_FIRST);
94 for(int i = 1; i <= N; ++i)val[i] = A[i], val[i + N] = B[i];
95 // for(int i = 1; i <= N2; ++i)printf("VAL %d : %d | %d | %d\n", i, val[i].a, val[i].b, val[i].idx);
96 for(int i = 1; i <= N; ++i){
97 int ptl = distance(B + 1, lower_bound(B + 1, B + N + 1, Node{A[i].a - D, 0, 0}, CMP_FIRST) + 1);
98 int ptr = distance(B + 1, upper_bound(B + 1, B + N + 1, Node{A[i].a, 0, 0}, CMP_FIRST));
99 st.Connect(leaf[i], ptl + N, ptr + N);
100 ptl = distance(A + 1, lower_bound(A + 1, A + N + 1, Node{0, B[i].b - D, 0}, CMP_SECOND) + 1);
101 ptr = distance(A + 1, upper_bound(A + 1, A + N + 1, Node{0, B[i].b, 0}, CMP_SECOND));
102 st.Connect(leaf[i + N], ptl, ptr);
103 }bfs();
104 for(int i = 1; i <= N; ++i)printf("%d\n", ans[i] == 0x3f3f3f3f ? -1 : ans[i]);
105 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
106 return 0;
107}
108
109template < typename T >
110inline T read(void){
111 T ret(0);
112 short flag(1);
113 char c = getchar();
114 while(c != '-' && !isdigit(c))c = getchar();
115 if(c == '-')flag = -1, c = getchar();
116 while(isdigit(c)){
117 ret *= 10;
118 ret += int(c - '0');
119 c = getchar();
120 }
121 ret *= flag;
122 return ret;
123}
给定
Input_1
xxxxxxxxxx
514 1
21 2
31 3
41 4
54 3
Output_1
xxxxxxxxxx
118
经典 树形DP 水题,开始感觉和 COCI 的那个 基环树DP 挺像,但是吧这玩意比那道题要简单得多。
不难想到设
xxxxxxxxxx
791
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23
24
25template< typename T = int >
26inline T read(void);
27
28int N, K;
29struct Edge{
30 Edge* nxt;
31 int to;
32 OPNEW;
33}ed[210000];
34ROPNEW(ed);
35Edge* head[110000];
36int dp[110000][4];
37
38void dfs(int p = 1, int fa = 0){
39 for(auto i = head[p]; i; i = i->nxt){
40 if(SON == fa)continue;
41 dfs(SON, p);
42 dp[p][1] = ((ll)dp[p][1] * ((ll)dp[SON][2] + dp[SON][3])) % MOD;
43 dp[p][2] = ((ll)dp[p][2] * ((ll)dp[SON][1] + dp[SON][3])) % MOD;
44 dp[p][3] = ((ll)dp[p][3] * ((ll)dp[SON][1] + dp[SON][2])) % MOD;
45 }
46}
47
48int main(){
49 N = read(), K = read();
50 for(int i = 1; i <= N - 1; ++i){
51 int s = read(), t = read();
52 head[s] = new Edge{head[s], t};
53 head[t] = new Edge{head[t], s};
54 }
55 for(int i = 1; i <= N; ++i)for(int j = 1; j <= 3; ++j)dp[i][j] = 1;
56 for(int i = 1; i <= K; ++i){
57 int p = read(), c = read();
58 for(int j = 1; j <= 3; ++j)if(j != c)dp[p][j] = 0;
59 }dfs();
60 printf("%lld\n", ((ll)dp[1][1] + dp[1][2] + dp[1][3]) % MOD);
61 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
62 return 0;
63}
64
65template < typename T >
66inline T read(void){
67 T ret(0);
68 short flag(1);
69 char c = getchar();
70 while(c != '-' && !isdigit(c))c = getchar();
71 if(c == '-')flag = -1, c = getchar();
72 while(isdigit(c)){
73 ret *= 10;
74 ret += int(c - '0');
75 c = getchar();
76 }
77 ret *= flag;
78 return ret;
79}
给定两个长度为
Input_1
xxxxxxxxxx
615 10
24 10
36 15
43 5
54 9
63 6
Output_1
xxxxxxxxxx
119
一道小清新蓝色水题,思维简单代码极短,做起来很舒服。
不难想到二分答案,二分
当然线段树猫树树状数组之类的东西维护也一样,但是 duck不必。
复杂度
xxxxxxxxxx
671
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26int N;
27ll M;
28int F[110000], S[110000];
29
30bool Check(int lim){
31 ll sum(0);
32 for(int i = 1; i <= N; ++i){
33 if(S[i] > lim){sum = 0; continue;}
34 sum += F[i];
35 if(sum >= M)return true;
36 }return false;
37}
38
39int main(){
40 N = read(), M = read < ll >();
41 int mx(-1);
42 for(int i = 1; i <= N; ++i)F[i] = read(), S[i] = read(), mx = max(mx, S[i]);
43 int l = 1, r = mx, ans(-1);
44 while(l <= r){
45 int mid((l + r) >> 1);
46 if(Check(mid))ans = mid, r = mid - 1;
47 else l = mid + 1;
48 }printf("%d\n", ans);
49 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
50 return 0;
51}
52
53template < typename T >
54inline T read(void){
55 T ret(0);
56 short flag(1);
57 char c = getchar();
58 while(c != '-' && !isdigit(c))c = getchar();
59 if(c == '-')flag = -1, c = getchar();
60 while(isdigit(c)){
61 ret *= 10;
62 ret += int(c - '0');
63 c = getchar();
64 }
65 ret *= flag;
66 return ret;
67}
给定
Input_1
xxxxxxxxxx
213
21 2 0
Output_1
xxxxxxxxxx
111
还是二分“答案”,显然如果一只犇无法变成合法,那么它之后的所有犇都无法被变成合法的,所以这东西就算是有单调性了,于是我们考虑二分第几只犇是第一个不合法的,然后验证的时候把这只犇前面的所有犇的
xxxxxxxxxx
681
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26int N;
27int c[110000];
28
29bool Check(int pos){
30 std::priority_queue < int, vector < int >, greater < int > > q;
31 for(int i = 1; i < pos; ++i)q.push(c[i]);
32 int cur(N - pos);
33 while(!q.empty()){
34 int tp = q.top(); q.pop();
35 if(tp > cur)return false;
36 else ++cur;
37 }return true;
38}
39
40int main(){
41 N = read();
42 for(int i = 1; i <= N; ++i)c[i] = read();
43 int l = 1, r = N, ans = -1;
44 while(l <= r){
45 int mid = (l + r) >> 1;
46 if(!Check(mid))ans = mid, r = mid - 1;
47 else l = mid + 1;
48 }
49 printf("%d\n", N - ans + 1);
50 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
51 return 0;
52}
53
54template < typename T >
55inline T read(void){
56 T ret(0);
57 short flag(1);
58 char c = getchar();
59 while(c != '-' && !isdigit(c))c = getchar();
60 if(c == '-')flag = -1, c = getchar();
61 while(isdigit(c)){
62 ret *= 10;
63 ret += int(c - '0');
64 c = getchar();
65 }
66 ret *= flag;
67 return ret;
68}
SAM 和 SA,跳!
这场里最有意思的题来了
给定 A
),另外一个格子里有个木箱(B
),某些格子里是障碍(#
),剩下的是空地(.
),sssmzy 不能和木箱和障碍在同一个格子内,按照一般的推箱子游戏规则,
Input_1
xxxxxxxxxx
1015 5 4
2##.##
3##.##
4A.B..
5##.##
6##.##
73 2
83 5
91 3
105 3
Output_1
xxxxxxxxxx
41NO
2YES
3NO
4NO
似乎是一道显然的圆方树(不会),但是圆方树看着太不联赛了,于是我们考虑把圆方树的思想换一种说法,这道题就和圆方树“没有关系”了。
bfs 会吧,tarjan 点双缩点会吧,好的现在你会圆方树了。
我们发现如果没有这个 nt 的箱子,这题就能变成无脑 bfs 了,但是这个破箱子每个时候都会阻挡一些道路让我们能走的位置变化,仔细思考一下发现这个东西似乎有点像割点,也就是一个障碍物让本来可以走的路径变得不能走了(所以这是正常人能联想到的吗。。
所以我们考虑当我们在一个箱子相邻的四个位置时,想让一个箱子被推动的时候,比如我们想让箱子向上移动一格,那么我们要么在最初在箱子的下面,要么在上左右且在有这个箱子阻挡的情况下可以从对应位置到箱子下面,所以我们此时可以使用人类智慧考虑一个 DP 状态,令
显然当你从箱子四个方向相邻的某个位置到另一个相邻箱子四个方向的位置的时候一定有一条路径是通过箱子直接过去的,但是这条路径已经被堵死了,我们要求的就是是否还存在另一条可行的路径,然后这玩意你嗯跑肯定过不去,所以发挥人类智慧之后不难想到这东西可以转化为两点之间有至少两条无重点的简单路径,这东西不就是点双吗。
于是我们考虑给这图进行点双缩点(建图不难想到吧,每个方格向四个方向建边,当然不用真正建出来),然后记录每个点都在哪些点双里,开个 vector
就行,每次判断暴力枚举一遍两个点是否有相同的点双,有的话就说明两点在同一点双里,有两个或以上的路径,去掉箱子占用的一条依然可以转移。
对于最初,显然我们很有可能离箱子很远,这个时候我们可以跑一遍无脑的 bfs,记录一下能够到达箱子四个方向的哪几个方向,并以此为 DP 的边界,然后跑一下 Tarjan点双缩点,最后的 DP 过程可以通过一个类似记忆化搜索的宽搜实现,也就是从箱子的四个方向的状态向其它点扩展,具体转移之前已经说过了。
然后还有一个坑,就是有可能最初我们根本无法到达箱子周围,这样我们会全部输出 NO
,但是如果某次询问恰好是箱子原本的位置这个东西也应该是可行的,所以特判一下就好。
然后注意精细实现,如果写的比较丑的话码量会额外大很多,写的不是特别丑的话 5KiB
左右就够了。
xxxxxxxxxx
1531
2
3
4
5
6
7
8
9
10
11using namespace std;
12using namespace __gnu_pbds;
13
14mt19937 rnd(random_device{}());
15int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
16bool rnddd(int x){return rndd(1, 100) <= x;}
17
18typedef unsigned int uint;
19typedef unsigned long long unll;
20typedef long long ll;
21typedef long double ld;
22
23template< typename T = int >
24inline T read(void);
25
26struct Coord{
27 int x, y;
28 friend const bool operator == (const Coord &a, const Coord &b){return a.x == b.x && a.y == b.y;}
29};
30int N, M, Q;
31Coord S, T;
32char mp[1600][1600];
33int dx[10] = {0, -1, 0, 1, 0};
34int dy[10] = {0, 0, 1, 0, -1};
35bool dire[10];
36bool vis[1600][1600];
37bool reach[1600][1600][5];
38int dfn[1600][1600], low[1600][1600];
39bool inS[1600][1600];
40vector < int > belong[1600][1600];
41int cnt(0), BCC(0);
42
43
44void bfs_pre(void){
45 queue < Coord > cur; cur.push(S); vis[S.x][S.y] = true;
46 while(!cur.empty()){
47 auto tp = cur.front(); cur.pop();
48 for(int i = 1; i <= 4; ++i){
49 int tx(tp.x + dx[i]), ty(tp.y + dy[i]);
50 if(CHECK(tx, ty) && !vis[tx][ty] && mp[tx][ty] ^ '#' && mp[tx][ty] ^ 'B'){
51 vis[tx][ty] = true;
52 for(int j = 1; j <= 4; ++j)
53 if(tx == T.x + dx[j] && ty == T.y + dy[j])dire[j] = true;
54 cur.push(Coord{tx, ty});
55 }
56 if(dire[1] && dire[2] && dire[3] && dire[4])return;
57 }
58 }
59}
60
61void Tarjan(int x, int y){
62 static stack < Coord > cur;
63 dfn[x][y] = low[x][y] = ++cnt;
64 cur.push(Coord{x, y}); inS[x][y] = true;
65 for(int i = 1; i <= 4; ++i){
66 int tx(x + dx[i]), ty(y + dy[i]);
67 if(!CHECK(tx, ty) || mp[tx][ty] == '#')continue;
68 if(!dfn[tx][ty]){
69 Tarjan(tx, ty);
70 low[x][y] = min(low[x][y], low[tx][ty]);
71 if(low[tx][ty] >= dfn[x][y]){
72 ++BCC;
73 while(true){
74 auto tp = cur.top(); cur.pop();
75 belong[tp.x][tp.y].emplace_back(BCC);
76 inS[tp.x][tp.y] = false;
77 if(tp == Coord{tx, ty})break;
78 }belong[x][y].emplace_back(BCC);
79 }
80 }else if(inS[tx][ty])
81 low[x][y] = min(low[x][y], dfn[tx][ty]);
82 }
83}
84bool InSameBSS(Coord A, Coord B){
85 for(auto i : belong[A.x][A.y])
86 for(auto j : belong[B.x][B.y])
87 if(i == j)return true;
88 return false;
89}
90struct Status{int x, y; int dire;};
91
92void bfs(void){
93 queue < Status > cur;
94 for(int i = 1; i <= 4; ++i)if(dire[i])cur.push(Status{T.x, T.y, i}), reach[T.x][T.y][i] = true;
95 while(!cur.empty()){
96 auto tp = cur.front(); cur.pop();
97 for(int i = 1; i <= 4; ++i){
98 int tx(tp.x + dx[i]), ty(tp.y + dy[i]);
99 if(
100 CHECK(tx, ty) &&
101 mp[tx][ty] ^ '#' &&
102 (
103 OPPOSITE(i) == tp.dire ||
104 InSameBSS(
105 Coord{tp.x + dx[tp.dire], tp.y + dy[tp.dire]},
106 Coord{tp.x + dx[OPPOSITE(i)], tp.y + dy[OPPOSITE(i)]}
107 )
108 ) &&
109 !reach[tx][ty][OPPOSITE(i)]
110 ){
111 reach[tx][ty][OPPOSITE(i)] = true;
112 cur.push(Status{tx, ty, OPPOSITE(i)});
113 }
114 }
115 }
116}
117
118int main(){
119 N = read(), M = read(), Q = read();
120 char c = getchar();
121 for(int i = 1; i <= N; ++i)for(int j = 1; j <= M; ++j){
122 while(c != '#' && c != '.' && c != 'A' && c != 'B')c = getchar();
123 mp[i][j] = c;
124 if(mp[i][j] == 'A')S = Coord{i, j};
125 if(mp[i][j] == 'B')T = Coord{i, j};
126 c = getchar();
127 }bfs_pre();
128 Tarjan(S.x, S.y);
129 bfs();
130 while(Q--){
131 int x = read(), y = read();
132 if(Coord{x, y} == T){printf("YES\n"); continue;}
133 printf("%s\n", (reach[x][y][1] || reach[x][y][2] || reach[x][y][3] || reach[x][y][4]) ? "YES" : "NO");
134 }
135 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
136 return 0;
137}
138
139template < typename T >
140inline T read(void){
141 T ret(0);
142 short flag(1);
143 char c = getchar();
144 while(c != '-' && !isdigit(c))c = getchar();
145 if(c == '-')flag = -1, c = getchar();
146 while(isdigit(c)){
147 ret *= 10;
148 ret += int(c - '0');
149 c = getchar();
150 }
151 ret *= flag;
152 return ret;
153}
update-2022_11_02 初稿