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
";
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
";
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
", 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
", 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
";
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
", 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
", 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?'
':' ');
90     }
91     return 0;
92 }
View Code

L. Vacation Plans

题意:

原文地址:https://www.cnblogs.com/skyette/p/8271006.html