2017-2018 ACM-ICPC, Asia Daejeon Regional Contest


先來一個大神的全題解:http://blog.myungwoo.kr/121

 

A. Broadcast Stations

 

B. Connect3

題意:在一個4*4的棋盤上玩游戲,先手執黑,后手執白。每次一位玩家選一個未填滿的column,在其中row id最小的位置放下自己的棋子。如果連續的三個棋子同色(同行/同列/同對角線),游戲結束,下最后一步的人獲勝。下面給你一組詢問,(x, a, b) (1 <= x, a, b <= 4),問你先手第一步下(1, x),且游戲的最后一步是后手下白棋在(a, b)時,棋盤的最終形態有多少種。

思考:發現可以用三進制數(3^16)表示棋盤,0表示空,1表示黑棋,2表示白棋。暴力搜索即可。如果是多組詢問,還可以把4*4*4中詢問的結果本地打表。

代碼:

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 typedef  long long ll;
  5 typedef pair<int, int> ii;
  6 typedef pair<ll, ll> l4;
  7 typedef pair<ii, int> iii;
  8 #define mp make_pair
  9 #define pb push_back
 10 
 11 const int N = 43046721;
 12 int p[16];
 13 int ans[4][4][4]={0};
 14 int d[N]={0};
 15 int x;
 16 inline int getp(int x, int y)
 17 {
 18   return p[x*4+y];
 19 }
 20 inline int get(const int &bit, int x, int y)
 21 {
 22   int a = x*4+y;
 23   return bit/p[a]%3;
 24 }
 25 inline bool check(const int &bit, int x, int y, int win=2, bool print=false)
 26 {
 27   if (print)
 28     cerr << "check " << x << " " << y << " " << win << endl;
 29   //check vertical
 30   bool work;
 31 
 32   for (int i = max(0, x-2); i <= min(x, 1); ++i)
 33      {
 34       work = true;
 35       for (int j = 0; j < 3; ++j)
 36     if (get(bit, i+j, y) != win)
 37       {
 38         work = false;
 39         break;
 40       }
 41       if (work)
 42     {
 43       if (print) cerr << i << " to " << x+2 << " at y = " << y << endl;
 44       return true;
 45     }
 46     }
 47   //check horizontal
 48   for (int i = max(0, y-2); i <= min(y, 1); ++i)
 49     {
 50       work = true;
 51       for (int j = 0; j < 3; ++j)
 52     if (get(bit, x, i+j) != win)
 53       {
 54         work = false;
 55         break;
 56       }
 57       if (work)
 58     {
 59       if (print) cerr << "at x = "<< x << " " << i << " to " << i+2 << endl;
 60       return true;
 61     }
 62     }
 63   //check dia
 64   int tx = x, ty = y;
 65   while (tx > 0 && ty > 0)
 66     --tx, --ty;
 67   while (tx+2 < 4 && ty+2<4)
 68     {
 69       work = true;
 70       for (int j = 0; j < 3; ++j)
 71     if (get(bit, tx+j, ty+j) != win)
 72       {
 73         work = false;
 74         break;
 75       }
 76       if (work)
 77     {
 78       if (print) cerr << tx << " to " << tx+2 << " and " << ty << " " << ty+2 << endl;
 79       return true;
 80     }
 81       ++tx, ++ty;
 82     }
 83   //check anti dia
 84   tx = x, ty = y;
 85   while (tx > 0 && ty < 3)
 86     --tx, ++ty;
 87   while (tx+2<4 && ty-2>=0)
 88     {
 89       work = true;
 90       for (int j = 0; j < 3; ++j)
 91     if (get(bit, tx+j, ty-j) != win)
 92       {
 93         work = false;
 94         break;
 95       }
 96       if (work)
 97     {
 98       if (print) cerr << tx << " to " << tx+2 << " and " << ty << " " << ty-2 << endl;
 99       return true;
100     }
101       ++tx, --ty;
102     }
103   return false;
104 }
105 void print(int cur)
106 {
107   return;
108   cerr << endl << "print\n";
109   for (int i = 0; i < 4; ++i, cerr << endl)
110     for (int j = 0; j < 4; ++j)
111       {
112     cerr << get(cur, i, j);
113       }
114 }
115 int solve(int xx, int aa, int bb)
116 {
117   int ret = 0;
118   x = xx;
119   memset(d, 0, sizeof(d));
120   queue<int> q, nq;
121   int cur = 0;
122   cur = 1*getp(0, xx);
123   q.push(cur);
124   d[cur] = -1;
125   set<int> st;
126   while (!q.empty())
127     {
128       while (!q.empty())
129     {
130       int cur = q.front();
131       q.pop();
132       if (get(cur, aa, bb) != 0)
133         continue;
134 
135       for (int j = 0; j < 4; ++j)
136         for (int i = 0; i < 4; ++i)
137           if (get(cur, i, j) == 0)
138         {
139           //          cerr << "can go " << i << "," << j << endl;
140           int nxt = cur + 2 * getp(i, j);
141           if (check(nxt, i, j))
142             {
143               if (i == aa && j == bb)
144             {
145               st.insert(nxt);
146             }
147             }
148           else
149             {
150               if (d[nxt])
151             break;
152               else
153             {
154               d[nxt] = -1;
155               nq.push(nxt);
156             }
157             }
158           break;
159         }
160     }
161       //      cerr << "nq\n";
162       while (!nq.empty())
163     {
164       int cur = nq.front();
165       nq.pop();
166       if (get(cur, aa, bb) != 0)
167         continue;
168 
169       for (int j = 0; j < 4; ++j)
170         for (int i = 0; i < 4; ++i)
171           if (get(cur, i, j) == 0)
172         {
173           int nxt = cur + getp(i, j);
174           if (d[nxt])
175             break;
176           if (check(nxt, i, j, 1))
177             break;
178           d[nxt] = -1;
179           q.push(nxt);
180           break;
181         }
182     }
183     }
184   /*
185   for (int i = 0; i < 200; ++i)
186     {
187       cerr << i << endl;
188       check(*st.begin(), aa, bb, 2, true);
189       print(*st.begin());
190       st.erase(st.begin());
191     }
192   */
193   return st.size();
194 }
195 int main()
196 {
197   p[0] = 1;
198   for (int i = 1; i < 16; ++i)
199     p[i] = p[i-1]*3;
200   assert(N == p[15]*3);
201   int a, b, c;
202   scanf("%d %d %d", &c, &a, &b);
203   printf("%d\n", solve(c-1, a-1, b-1));
204 }
View Code

賽后:

比賽中寫的很麻煩,主要是因為一開始讀錯了題,沒有看到每一步只能下在一個column的最低位置。現在想想,可不可以直接枚舉棋盤的終止狀態,判斷(a, b)是否可以是最后一步即可。

但其實實現發現有很多細節需要考慮,主要是檢查當前棋盤是否是一個合法的游戲狀態。最好的方法還是由合法狀態bfs到其他狀態

 

C. Game Map

題意:n <= 1e5 個點,m <= 3e5 條邊的無向圖。從任意一點出發,只能向度數比當前點的度數大的點走。求最長的路徑上有多少個點。

觀察:要求路徑上點度數嚴格遞增,其實已經給邊定了方向且無環。所以本質就是DAG上的最長路,記憶畫搜索即可。

代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef  long long ll;
 5 typedef pair<int, int> ii;
 6 typedef pair<ll, ll> l4;
 7 typedef pair<ii, int> iii;
 8 #define mp make_pair
 9 #define pb push_back
10 
11 
12 
13 const int maxn = 1e5+1;
14 int n, m;
15 vector<int> g[maxn];
16 int d[maxn];
17 
18 int dp(int cur)
19 {
20   int &ret = d[cur];
21   if (ret == -1)
22     {
23       ret = 1;
24       for (int i = 0; i < g[cur].size(); ++i)
25     {
26       int nxt = g[cur][i];
27       if (g[nxt].size() > g[cur].size())
28         ret = max(ret, 1+dp(nxt));
29     }
30     }
31   return ret;
32 }
33 
34 
35 int main()
36 {
37   scanf("%d %d", &n, &m);
38   for (int i = 0; i < m; ++i)
39     {
40       int a, b;
41       scanf("%d %d", &a, &b);
42       g[a].pb(b);
43       g[b].pb(a);
44     }
45   memset(d, -1, sizeof(d));
46   int ans = 1;
47   for (int i = 0; i < n; ++i)
48     ans = max(ans, dp(i));
49   printf("%d\n", ans);
50 }
View Code

 

D. Happy Number

題意:f(n) = n各數位平方的和。如果存在一個k使得f^k(n) = 1, 那么就說n是happy的。可以發現,如果n不是happy的,那么一定存在一個p使得f^p(n) = n。下面給你一個詢問n<=1e9,讓你輸出他是否happy。

觀察:對於n <= 1e9, f(n) <= max(9*9^2, f(1e9)) = 9^3。直接暴力計算,遇到循環終止。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 
 5 bool vis[1000];
 6 int f(int n)
 7 {
 8   int ret = 0;
 9   while (n)
10     {
11       int tmp = n%10;
12       n /= 10;
13       ret += tmp * tmp;
14     }
15   return ret;
16 }
17 bool solve(int n)
18 {
19   if (n == 1)
20     return true;
21   if (vis[n])
22     return false;
23   vis[n] = true;
24   return solve(f(n));
25 }
26 int main()
27 {
28   memset(vis, 0, sizeof(vis));
29   int n;
30   scanf("%d", &n);
31   n = f(n);
32   puts(solve(n)?"HAPPY":"UNHAPPY");
33 }
View Code

 

E. How Many to Be Happy

題意:邊帶權無向圖G,對於一條邊e,如果存在一個包含e的最小生成樹,那么e就是happy的。定義H(e) 為 使e變為happy所需刪掉的最少邊數。輸入一個n <= 100個點m<=500條邊的圖,讓你輸出所有邊的H(e)之和。

觀察:對於一條邊e(x, y, len),只有長度小len的邊才會對H(e)產生影響(等於len的邊不影響,可以考慮kruskal算法中,邊權相同的邊的位置可以隨便更改)。而且邊數點數都不多,也許可以每條邊分別處理。那么考慮所有長度小於len的邊的生成子圖SubG,若想要e存在於某個mst,就要刪掉最小的邊使SubG中x和y不聯通,可以發現就是要求最小割。

 

F. Philosphoer's Walk

題意:

觀察:大體可以看出是個recursion。

 

G. Rectilinear Regions

題意:給你兩條階梯線U和L,階梯線是指非遞增或者非遞減的折線。每條階梯線的拐點個數不超過25e3。保證兩條直線的拐點沒有重復的x或者y,讓你求出U在上方L在下方的封閉空間的個數和面積和。

觀察:首先如果L和U的方向不同(一增一減),直接輸出“0 0”。同減的情況可以轉化成同增,方法是先把兩條邊上所有的y取相反數,然后交換U和L兩條線。這樣只需要考慮同增的情況。用一個sorted vector來保存拐點就好。小心一開始U就高於L的特殊情況,還有小心最后U高於L的情況。第一個特殊情況我是用一個bool first = true來標記,當L高於U的時候first = false,只有當first == false的時候才開始計算答案。第二個特殊情況我是記錄一個tmparea,表示當前空間的面積,每當L高於U使得面積閉合的時候,我在吧tmparea更新到答案(同時更新封閉區間的個數)。

代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef  long long ll;
 5 typedef pair<int, int> ii;
 6 typedef pair<ll, ll> l4;
 7 typedef pair<ii, int> iii;
 8 #define mp make_pair
 9 #define pb push_back
10 
11 
12 const int N = 25e3+10;
13 struct Node
14 {
15   int x, y, id;
16   bool operator<(const Node &r) const
17   {
18     return x < r.x;
19   }
20   void read(int xx)
21   {
22     scanf("%d %d", &x, &y);
23     id =xx ;
24   }
25 } a[N], b[N], c[N<<1];
26 
27 
28 int main()
29 {
30   int n, m;
31   int y0, y1;
32   scanf("%d %d", &n, &m);
33   scanf("%d", &y0);
34   for (int i = 0; i < n; ++i)
35     a[i].read(0);
36   scanf("%d", &y1);
37   for (int i = 0; i < m; ++i)
38     b[i].read(1);
39   bool d1 = a[0].y > y0;
40   bool d2 = b[0].y > y1;
41   if (d1 != d2)
42     {
43       puts("0 0");
44       return 0;
45     }
46   else if (d1 == 0)
47     {
48       y0 *= -1;
49       y1 *= -1;
50       for (int i = 0; i < n; ++i)
51     a[i].y *= -1, a[i].id = 1;
52       for (int j = 0; j < m; ++j)
53     b[j].y *= -1, b[j].id = 0;
54       swap(y0, y1);
55       swap(a, b);
56       swap(n, m);
57     }
58     
59   //  cerr << "read done\n";
60   merge(a, a+n, b, b+m, c);
61   int last=-1;
62   ll ans = 0;
63   ll tmp = 0;
64   int ansans = 0;
65   bool first = y1 > y0;
66   for (int i = 0; i < n+m; ++i)
67     {
68       if (c[i].id == 0)
69     {
70       if (last != -1)
71         {
72           tmp += 1ll*(c[i].x-last)*(y1-y0);
73           if (c[i].y < y1)
74         last = c[i].x;
75           else
76         last = -1, ++ansans, ans += tmp, tmp = 0;
77         }
78       y0 = c[i].y;
79       if (y0 >= y1)
80         first = false;
81     }
82       else
83     {
84       if (last != -1)
85         {
86           tmp += 1ll*(c[i].x-last)*(y1-y0);
87           last = c[i].x;
88         }
89       y1 = c[i].y;
90       if (!first && last == -1 && y1 > y0)
91         {
92           last = c[i].x;
93         }
94     }
95       //      cerr << c[i].id << " " << c[i].x << " " << c[i].y << " " << c[i].id <<" " << y0 << " " << y1 << " " << last <<  endl;
96     }
97   printf("%d %lld\n", ansans, ans);
98 }
View Code

 

H. Rock Paper Scissors

大坑

 

I. Slot Machines

題意:給你一個長度為n<= 1e6的整數序列T[1-n],找到使得pair(k+p, p)最小的k和p,k和p要滿足, T[i+p] = T[i] (k < i <= n-p)。

方法:觀察可知,對於一組(k, p),p為T[k+1, ..., n] 的周期,取最小的循環周期最優。而通過kmp可以O(n)預處理fail數組f[] 然后O(1)詢問任意前綴的循環節長度(i-f[i]),所以只需要對T[1-n]反向求kmp得到fail數組,枚舉k,p可以快速求出,更新答案。

代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef  long long ll;
 5 typedef pair<int, int> ii;
 6 typedef pair<ll, ll> l4;
 7 typedef pair<ii, int> iii;
 8 #define mp make_pair
 9 #define pb push_back
10 
11 
12 
13 
14 const int maxn = 1e6+10;
15 int a[maxn], f[maxn], n;
16 int main()
17 {
18   scanf("%d", &n);
19   for (int i = n-1; i >= 0; --i)
20     {
21       scanf("%d", a+i);
22     }
23   f[0] = -1;
24   for (int i = 0, j = -1; i < n; ++i, ++j)
25     {
26       while (j != -1 && a[j] != a[i])
27     j = f[j];
28       f[i+1] = j+1;
29     }
30   int k = n, p = 1;
31   for (int i = n; i >= 1; --i)
32     {
33       int kk = n-i;
34       int pp = i-f[i];
35       if (pp + kk < k+p)
36     k = kk, p = pp;
37       else if (pp + kk == k+p && pp < p)
38     k = kk, p = pp;
39     }
40   printf("%d %d\n", k, p);
41 }
View Code

 

J. Strongly Matchable

 

K. Untangling Chain

題意:給你一個有n-1個拐點的折線 (n <= 1e5),要求你妥善安排每一個線段的長度,使得折線段不會自交。每一條線段的長度不能超過n。

方法:如果n不是很大,可以使第i段線段的長度為2^i,這樣一定不會自交。此時思路變成,可不可以使邊長不斷增大,使得后續的轉彎不會受之前的影響。

想了想可不可以直接把線段長度設成1-n,發現不行,比如假設從(0, 0)開始 右(1, 0) 上(1, 2) 右(1+3, 2) 上(1+3, 2+4) 右(1+3+5, 2+4) 下(1+3+5, 2+4-6) 左(1+3+5-7, 0)自交了。即如果在水平方向上(豎直方向類似),如果連續的幾次運動都是朝着同一個自方向,那么我們的方案會使得這個方向的距離快速增加(即上述水平方向上連續三次向右的操作,總的看是連續向右了1+3+5 = 9個單位),那么下一次向反方向的操作(如上述的向左),就需要一個很大的長度(上述例子中至少需要9+1)。

然后就想到了正確的做法。分開考慮水平方向和豎直方向。對於一個方向,考慮這個方向上第i次移動,如果方向和上一次相同,那么就走1的長度,不然就走i的長度。可以發現這樣走,不管怎么樣都不會自交。例子: 右上右上右下左 : 右(1, 0)上(1, 1)右(1+1, 1)上(2, 1+1)右(2+1, 2)下(3, 2-3)左(3-4, -1)

代碼:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10005;
 4 const int dx[4]={0,0,-1,1};
 5 const int dy[4]={-1,1,0,0};
 6 int n,dir[maxn],s,lstx,lsty,res[maxn],ansx=1,ansy=1;
 7 inline void change(int dir, int &dx, int &dy)
 8 {
 9   if (dir == 1)
10     {
11       int ddx = -dy;
12       int ddy = dx;
13       dy = ddy;
14       dx = ddx;
15     }
16   else
17     {
18       int ddx = dy;
19       int ddy = -dx;
20       dy = ddy;
21       dx = ddx;
22     }
23 }
24 int main()
25 {
26     scanf("%d",&n);
27     for (int i=0;i<n;++i)
28         scanf("%d%d",&s,&dir[i]);
29     if (n==1)
30         puts("1");
31     else if (n==2)
32         puts("1 2");
33     else {
34       int dx = 1, dy = 0;
35       res[0] = 1;
36       int last = 1;
37       int cal = 1;
38       for (int i = 1; i < n-1; i += 2)
39     {
40       change(dir[i-1], dx, dy);
41       change(dir[i], dx, dy);
42       ++cal;
43       if (dx == last)
44         {
45           res[i+1] = 1;
46         }
47       else
48         {
49           res[i+1] = cal;
50           last = dx;
51         }
52     }
53       dx = 1, dy = 0;
54       change(dir[0], dx, dy);
55       last = dy;
56       cal = 1;
57       res[1] = 1;
58       for (int i = 2; i < n-1; i += 2)
59     {
60       change(dir[i-1], dx, dy);
61       change(dir[i], dx, dy);
62       ++cal;
63       if (last == dy)
64         res[i+1] = 1;
65       else
66         res[i+1] = cal, last = dy;
67     }
68       res[n-1] = 1;
69       /*
70         lstx=dir[0],lsty=dir[1];
71         res[0]=res[1]=1;
72         for (int i=1;i<n;++i) {
73             if (i%2==0) {
74                 ++ansx;
75                 if (dir[i]==lstx)
76                     res[i]=1;
77                 else
78                     res[i]=ansx,lstx=-lstx;
79             } else {
80                 ++ansy;
81                 if (dir[i]==lsty)
82                     res[i]=1;
83                 else
84                     res[i]=ansy,lsty=-lsty;
85             }
86         }
87       */
88         for (int i=0;i<n;++i)
89             printf("%d%c",res[i],i+1==n?'\n':' ');
90     }
91     return 0;
92 }
View Code

 

L. Vacation Plans

題意:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM