2000-2001 ACM Northeastern European Regional Programming Contest
A. Flip Game
题意:4*4的黑白棋盘,每次选择一个格子,可以同时将该格子与上下左右相邻的格子颜色反转。问你要使棋盘变为全黑或者全白至少需要几步。无解输出Impossible。
观察:棋盘的状态不超过2^16种,把棋盘状态看作点,操作看成边,bfs计算初始状态到其他状态的最短路就好了。
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 const int N = 4; 28 const int maxn = N*N; 29 const int maxb = 1<<maxn; 30 int d[maxb]; 31 int pos(int a, int b) 32 { 33 return a*N+b; 34 } 35 int get(int &a, int b, int c) 36 { 37 return (a>>pos(b, c))&1; 38 } 39 int flip(int state, int a, int b) 40 { 41 int mask = 1<<pos(a, b); 42 if (a > 0) 43 mask ^= 1<<pos(a-1, b); 44 if (a+1 < N) 45 mask ^= 1<<pos(a+1, b); 46 if (b > 0) 47 mask ^= 1<<pos(a, b-1); 48 if (b+1 < N) 49 mask ^= 1<<pos(a, b+1); 50 return state^mask; 51 } 52 int read() 53 { 54 char c; 55 int ret = 0; 56 int pos = 0; 57 while (~scanf("%c", &c)) 58 { 59 if (c != 'b' && c != 'w') 60 continue; 61 if (c == 'b') 62 ret ^= (1<<pos); 63 ++pos; 64 } 65 return ret; 66 } 67 int main() 68 { 69 memset(d, -1, sizeof(d)); 70 int start = read(); 71 queue<int> q; 72 d[start] = 0; 73 q.push(start); 74 while (!q.empty()) 75 { 76 int cur = q.front(); 77 q.pop(); 78 for (int i = 0; i < N; ++i) 79 for (int j = 0; j < N; ++j) 80 { 81 int nxt = flip(cur, i, j); 82 if (d[nxt] == -1) 83 { 84 d[nxt] = d[cur]+1; 85 q.push(nxt); 86 } 87 } 88 } 89 int ans = d[maxb-1]; 90 if (d[0] != -1) 91 if (ans == -1 || ans > d[0]) 92 ans = d[0]; 93 if (ans == -1) 94 puts("Impossible"); 95 else 96 printf("%d\n", ans); 97 98 }
B. Buffer Manager
题意:前缀和裸题
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 int n, k; 28 deque<int> v; 29 int ans=0, cnt = 1e9; 30 void solve(int pos) 31 { 32 v.push_front(0); 33 for (int i = 1; i < v.size(); ++i) 34 v[i] += v[i-1]; 35 for (int i = k; i < v.size(); ++i) 36 { 37 int tmp = v[i]-v[i-k]; 38 if (cnt > tmp) 39 { 40 cnt = tmp; 41 ans = pos+i-k; 42 } 43 } 44 } 45 int main() 46 { 47 scanf("%d %d", &n, &k); 48 ans = 0; 49 char c; 50 int pos = 0; 51 while (~scanf("%c", &c)) 52 { 53 if (!isdigit(c) && c != '*') 54 continue; 55 ++pos; 56 if (c == '*') 57 { 58 solve(pos-v.size()); 59 v.clear(); 60 } 61 else 62 { 63 v.pb(c-'0'); 64 } 65 } 66 ++pos; 67 solve(pos-v.size()); 68 printf("%d\n", ans); 69 }
C. Triathlon
题意:铁人三项。有n个人,n不超过100。有三项运动,路程分别是A,B和C,对应的,第i个人作这三项的速度是a[i], b[i], c[i]。可以随意的安排A,B,C的大小,对于每一个人,让你输出,是否存在一种安排,使得该人完成三项运动的时间最短。
观察:对于一种安排(A, B, C), 第i个人的完成时间t[i] = A/a[i] + B/b[i] + C/c[i]。因为坐标可以放缩,可以直接考虑t[i] = 1的情况,1 = A/a[i] + B/b[i] + C/c[i] 。为了想让i赢,那么就是求不等式组:
1 <= A/a[i] + B/b[i] + C/c[i]
1 > A/a[j] + B/b[j] + C/c[j], for all j in [1, i) union (i, n]。
可以发现这是一个半平面交。
code:
D. Domino Puzzle
题意:一块1*2大小的domino骨牌是由两个数字组成,数字范围是1-6。下面给你不超过100块骨牌,允许你添加若干骨牌,使得已有的和你添加的骨牌可以拍成一排,且相邻骨牌的相邻数字相同。让你输出,添加骨牌上数字之和最小的添加骨牌的方案。
观察:
判定问题:假设我们已经选好了一些骨牌,那么判断能否把它们摆成题目要求的一排就是一个经典的欧拉路问题:把数字看作点,骨牌看作边,题目要求的一排其实就是一个欧拉路径。判断无向图欧拉路径就是两个条件:图连通;奇数度的点个数(因为图中 总度数==边数*2,所以奇数点个数一定是个偶数)不超过2。所以我们当我们确定加哪些边的时候,只需要用个dsu,或者dfs之类的东西,检查以上两个条件即可。
注意到,骨牌种类有6*(6-1)/2 + 6 = 15 + 6 = 21那么多,即C(6, 2)个数字不同的骨牌,和C(6, 1)个两个数字相同的骨牌。然后可以想到暴力枚举一下加哪些骨牌,而且只需要考虑加一个或者不加的情况,不需要考虑加超过1个的情况。原因很简单。首先,对于某个骨牌,加3个或者以上的个数肯定是浪费的。可以参考上面的判定,加三个或以上的该骨牌,不会改变连通性,且如果加奇数个边对点度数的影响和加一条是一样的,加偶数条边和加两条边的效果是一样的,所以三个或者以上的数量都可以不用考虑。然后为什么不需要考虑加两个的情况呢?通过上面的分析,我们可以知道,加边最关键的是改变了图的连通性。那么对于一个连通的图,我们如何加数字和最少的骨牌使这个图存在欧拉路呢?首先奇数点的个数肯定是偶数,为了加骨牌的数字和最小,我们只需要把数字最大的两个奇数点保留,把剩下的数字较小的奇数点两两配对即可,无论如何配对,数字和都是剩下的奇数点标号之和。
还有值得注意的一点是枚举新增的骨牌,不需要考虑6中数字相同的骨牌,因为他们不会对连通性甚至是点度数的奇偶性产生影响,但是输入中的这样的骨牌还是要记录一下的。
现在方法就成型了。枚举2^15所有新加边的可能(其实本质是枚举原图扩展出去的连通图),如果是连通图,通过上面讨论的方法统计额外需要加的骨牌数字和,然后更新答案。
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 const int N = 7; 28 29 int start[N]={0}; 30 namespace dsu 31 { 32 int p[N], degree[N]; 33 void init() 34 { 35 for (int i = 1; i < N; ++i) 36 p[i] = i, degree[i] = start[i]; 37 } 38 int pa(int id) 39 { 40 return id==p[id]?id:p[id]=pa(p[id]); 41 } 42 void merge(int x, int y) 43 { 44 ++degree[x], ++degree[y]; 45 p[pa(x)] = pa(y); 46 } 47 bool connected() 48 { 49 int root = 0; 50 for (root = 1; root < N; ++root) 51 if (degree[root]) 52 break; 53 assert(root < N); 54 for (int i = 1; i < N; ++i) 55 if (degree[i] && pa(i) != pa(root)) 56 return false; 57 return true; 58 } 59 int extra(vector<int> &v) 60 { 61 for (int i = 1; i < N; ++i) 62 if (degree[i]%2) 63 v.pb(i); 64 assert(v.size()%2==0); 65 if (!v.empty()) 66 v.pop_back(), v.pop_back(); 67 int ret = 0; 68 for (auto e : v) 69 ret += e; 70 return ret; 71 } 72 } 73 int id[N][N], cnt[N*N], tot; 74 ii res[N*N]; 75 /* 76 clock_t start; 77 void mark() 78 { 79 start = clock(); 80 } 81 inline void time() 82 { 83 return; 84 cerr << (double) (clock()-start)/CLOCKS_PER_SEC << endl; 85 } 86 */ 87 88 int main() 89 { 90 tot = 0; 91 for (int i = 1; i < N; ++i) 92 for (int j = i+1; j < N; ++j) 93 { 94 id[i][j] = tot; 95 res[tot] = mp(i, j); 96 cnt[tot] = 0; 97 ++tot; 98 } 99 //cerr << "tot state = " << tot << endl; 100 int n; 101 scanf("%d", &n); 102 for (int i = 0; i < n; ++i) 103 { 104 int a, b; 105 scanf("%d %d", &a, &b); 106 if (a == b) 107 { 108 start[a] += 2; 109 continue; 110 } 111 if (a > b) 112 swap(a, b); 113 ++cnt[id[a][b]]; 114 } 115 for (int i = 0; i < tot; ++i) 116 if (cnt[i]%2) 117 cnt[i]%=2; 118 else if (cnt[i]>2) 119 cnt[i] = 2; 120 int ans = 2e9; 121 vector<int> add; 122 for (int bit = (1<<tot)-1; bit >= 0; --bit) 123 { 124 //mark(); 125 if (bit == 0) 126 { 127 128 } 129 dsu::init(); 130 vector<int> tmpv; 131 int tmp = 0; 132 for (int i = 0; i < tot; ++i) 133 { 134 if ((bit>>i)&1) 135 { 136 tmpv.pb(i); 137 dsu::merge(res[i].first, res[i].second); 138 tmp += res[i].first+res[i].second; 139 } 140 for (int j = 0; j < cnt[i]; ++j) 141 dsu::merge(res[i].first, res[i].second); 142 } 143 if (!dsu::connected()) 144 continue; 145 vector<int> extra; 146 tmp += dsu::extra(extra); 147 for (int i = 0; i < extra.size(); i += 2) 148 tmpv.pb(id[extra[i]][extra[i+1]]); 149 if (tmp < ans) 150 { 151 ans = tmp; 152 add = tmpv; 153 } 154 //time(); 155 } 156 printf("%d\n", ans); 157 printf("%d\n", add.size()); 158 for (auto e : add) 159 printf("%d %d\n", res[e].first, res[e].second); 160 }
WA: 1. 没有考虑同数字的骨牌可以不用加。 2. 没有处理输入中同数字的骨牌。3. 没有想到,只需要枚举加或者不加(即枚举连通图),剩下的边权很容易计算)。
E. Binary Search
题意:题目中给你了一个二分程序,程序的结果和程序中的n和数列a[]有关。下面告诉你程序的结果i和程序二分的次数L,让你输出有哪些n可以使得存在一个a[]满足i和L。
观察:
发现n最大不过是1e4,不要被他问题中答案输出连续区间吓到了。所以,如果可以快速判断一个n是否可行,那我们只需枚举每个可能的n求解即可。
对于一个给定的n,我们可以模拟它的二分。一个很丑的做法是把所有二分L次后可能的区间都找出来,然后检查i是否是其中一个区间的中点。然而可以发现,每次二分我们只需要保留包含i的那个区间。所以这样从初始区间[0, n-1]走L步就好了。
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 int pos, L; 28 const int maxn = 1e4; 29 bool valid(int n) 30 { 31 int l = 0, r = n-1, mid; 32 for (int i = 1; i < L; ++i) 33 { 34 if (l >= r) 35 return false; 36 mid = (l+r)>>1; 37 if (pos == mid) 38 return false; 39 if (pos < mid) 40 r = mid-1; 41 else 42 l = mid+1; 43 } 44 return pos == (l+r)/2; 45 } 46 int main() 47 { 48 scanf("%d %d", &pos, &L); 49 vector<int> v; 50 for (int i = 1; i <= maxn; ++i) 51 if (valid(i)) 52 v.pb(i); 53 vector<ii> ans; 54 for (int i = 0; i < v.size(); ) 55 { 56 int nxt = i+1; 57 while (nxt < v.size() && v[nxt] == v[nxt-1]+1) 58 ++nxt; 59 ans.pb(mp(v[i], v[nxt-1])); 60 i = nxt; 61 } 62 printf("%d\n", ans.size()); 63 for (auto e : ans) 64 printf("%d %d\n", e.first, e.second); 65 }
F. Frontier
题意:给你不超过n (3 <= n <= 50) 个点围成的凸多边形(面积非空)的顶点坐标,点的顺序是顺时针。又给你m (m <= 1000) 个特殊点。让你找出一条顺时针的巡逻路线,a1 -> a2 -> ... -> ak -> a1,使得 a1 < a2 < ... < ak,巡逻路线围成的图形非空,且所有m个特殊点都被巡逻路线真包含(不能在巡逻路线的边界上)。问你巡逻路线最短是多少。
观察:
我是没想出来,看了别人的代码。
可以先考虑m=0的情况。因为要保持巡逻顺序,其实原问题就等价于选若干个点,形成的非空多边形,求最小的周长。答案比较明显,是三个不共线的点形成的三角形周长。为什么呢?首先要找k个点形成一个面积非空的多边形,那么k >= 3,且k个点不共线。如果k > 3,那么一定可以在这k个点中找出不共线的三点,而且这三点构成图形的变长更小(应该是三角形两边之和大于第三边的扩展,即simple polygon,任意一边小于其他边的和。证明方法应该是两点间线段长最短。如果不是的话可以参考一下划线部分的做法:证明方法应该是三角形划分。三角形划分对于任意simple polygon都存在。(感觉对nonsimple polygon应该也适用,想到一个证明方法就是把一个nonsimple polygon,分成若干个simple的polygon。那么对于原nonsimple polygon上的一条边L,它在被分割后的若干段L[1-k],在所属的simple polygon P[1-k]上,长度小于该simple polygon上其他边之和,length(L[i]) < length(P[i]) - length(L[i]),累加一下就有 Length(L) < sum(length(P[i])) - L <= 原nonsimple polygon周长 - length(L) = 原nonsimple polygon上除去L的线段长)。所以我们就n^3暴力的找三个不共线的点,用三角形的周长更新答案就好。
对于m>0的情况,我们要求巡逻路线的每一条边顺时针,且整个封闭折线包含所有特殊点,也就是对于一条边i->j, 所有特殊点都要i->j的右侧。因为初始的图形是凸多边形,按照 a1 < a2 < ... < ak的顺序,巡逻路线也是凸的,所以走完i->j,下一条边j->k肯定是要向右偏(或者直行,但是直行的情况可以不考虑,因为可以被i->k包括),所以如果有特殊点在i->j的左侧(如果在i->j上直接非法,因为不满足“真”包含),那么之后的曲线就不会包含这个点了。凭借这个观察,我们可以找出c(n, 2) = n*(n-1)/2中所有合法的边。此时,只要对这些边求一下最小的环,就是答案。
证明:我们令合法边构成的图为G,最小的环为C,一个环x的长度为len(x)。首先可以想到,最终答案的巡逻路线肯定G中的一个环,设为ans,即凸多边形环中最小的一个。因为C是最小环,所以len(C) <= len(ans)。其次可以发现,C是一个凸多边形环。为什么呢?我们现求一下C的凸包,设为C'。若C中所有点都在C'上,则C是凸的,证毕。如果C中有的点不在凸包C'上,比如有C中一段从i到j的折线不在凸包上,可以想到,把这段折线换成i->j,不仅不会减少覆盖的面积,既不会减少覆盖的特殊点,还会是总长度减少(原因就是对于任意多边形,一边的长度小于其他边之和)。所以len(C) >= len(ans)。得到len(C) = len(ans)。
最小环跑一下floyd就好。
code:
1 * 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 const int eps = 0; 28 int cmp(int a) 29 { 30 return a; 31 } 32 struct Point 33 { 34 int x, y; 35 Point(int x = 0, int y = 0):x(x), y(y) 36 {} 37 inline void read() 38 { 39 scanf("%d %d", &x, &y); 40 } 41 Point operator-(const Point &r) const 42 { 43 return Point(x-r.x, y-r.y); 44 } 45 double length() const 46 { 47 return sqrt(1.0*x*x+1.0*y*y); 48 } 49 }; 50 double dis(const Point &lhs, const Point &rhs) 51 { 52 return (lhs-rhs).length(); 53 } 54 int cross(const Point &lhs, const Point &rhs) 55 { 56 return lhs.x*rhs.y-lhs.y*rhs.x; 57 } 58 bool clockwise(const Point &a, const Point &b, const Point &c) 59 { 60 return cmp(cross(b-a, c-a)) < 0; 61 } 62 const int maxn = 50+5, maxm = 1e4+5; 63 int n, m; 64 Point a[maxn], b[maxm]; 65 double d[maxn][maxn], e[maxn][maxn]; 66 const double inf = 8e30; 67 bool test(int i, int j) 68 { 69 if (i == j) 70 return false; 71 for (int ii = 0; ii < m; ++ii) 72 if (!clockwise(a[i], a[j], b[ii])) 73 return false; 74 return true; 75 } 76 int main() 77 { 78 scanf("%d %d", &n, &m); 79 for (int i = 0; i < n; ++i) 80 a[i].read(); 81 for (int i = 0; i < m; ++i) 82 b[i].read(); 83 if (m == 0) 84 { 85 double ans = inf; 86 for (int i = 0; i < n; ++i) 87 for (int j = i+1; j < n; ++j) 88 for (int k = j+1; k < n; ++k) 89 if (cross(a[i]-a[j], a[j]-a[k])) 90 ans = min(ans, dis(a[i], a[j]) + dis(a[j], a[k]) + dis(a[k], a[i])); 91 printf("%.2lf\n", ans); 92 return 0; 93 } 94 for (int i = 0; i < n; ++i) 95 assert(test(i, (i+1)%n)); 96 for (int i = 0; i < n; ++i) 97 for (int j = 0; j < n; ++j) 98 d[i][j] = test(i, j)?dis(a[i], a[j]):inf; 99 100 for (int k = 0; k < n; ++k) 101 for (int i = 0; i < n; ++i) 102 for (int j = 0; j < n; ++j) 103 { 104 double tmp = d[i][k] + d[k][j]; 105 if (tmp < d[i][j]) 106 d[i][j] = tmp; 107 } 108 double ans = inf; 109 for (int i = 0; i < n; ++i) 110 ans = min(ans, d[i][i]); 111 //assert(ans < inf); 112 printf("%.2lf\n", ans); 113 }
WA:看了别人的代码还是错了很多次。主要是忘记特判三点不共线。还有就是int写成double。
G. Garland
题意:对于一个长度为n的double数列,n不超过1000。H[1] = A, (10 <= A <= 1000), H[n] = B, H[i] = (H[i-1]+H[i+1])/2 for (2 <= i <= n-1)。让你求出最小的B,使得H[i] > 0 for all i。
观察:推一下发现H[i+1] = 2*H[i] - H[i-1] + 2, 或者 (H[i+1]-H[i]) - (H[i]-H[i-1]) = 2,看出H[i]的通项公式是二次的,且最高项(即二次项)系数为2。可以发现,既然H[1]固定了,H[2]越大,接下来的所有H[i]越大。证明也比较简单,可以考虑二次函数的导数变化,也可以硬推:H[i+1]-H[i] = 2*(i-1) + H[2] - H[1]。 H[n] = H[1] + (H[2]-H[1]) + (H[3]-H[2]) + ... + (H[n]-H[n-1]) = H[1] + (n-1)*(H[2]-H[1] + H[n]-H[1])/2 = H[1] + (n-1)*(2*H[2] - 2*H[1] + 2*(n-1))/2,是个关于H[2]线性递增的式子。所以只需要在[0, H[1]]的区间里二分搜索一下H[2],求出H[2]的最小值,然后推出此时的H[n],就是H[n]的最小值。
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 int n; 28 double A; 29 30 int main() 31 { 32 scanf("%d %lf", &n, &A); 33 double l = 0, r = A, mid, ans=A; 34 while (l+1e-6 <= r) 35 { 36 mid = (l+r)/2; 37 bool work = true; 38 double a = A, b = mid; 39 for (int i = 1; i < n-1; ++i) 40 { 41 double c = 2*b+2-a; 42 if (c < 0) 43 work = false; 44 a = b, b = c; 45 } 46 if (work) 47 { 48 ans = b; 49 r = mid; 50 } 51 else 52 { 53 l = mid; 54 } 55 } 56 printf("%.2lf\n", ans); 57 }
H. Disk Tree
题意:给你若干条路径,让你输出路径树。
观察:直接建树,dfs输出就好
code:
1 /* 2 by skydog 3 */ 4 #include <iostream> 5 #include <cstdio> 6 #include <vector> 7 #include <utility> 8 #include <algorithm> 9 #include <cmath> 10 #include <cstring> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <deque> 16 #include <cassert> 17 #include <list> 18 using namespace std; 19 typedef long long ll; 20 typedef pair<int, int> ii; 21 typedef pair<ll, ll> l4; 22 23 #define mp make_pair 24 #define pb push_back 25 #define db(x) cerr << #x << " = " << x << endl 26 27 const int maxn = 1e5; 28 map<string, int> g[maxn]; 29 int sz = 0; 30 void dfs(int cur, int shift) 31 { 32 string space = string(shift, ' '); 33 for (const auto &pr : g[cur]) 34 { 35 cout << space << pr.first << '\n'; 36 dfs(pr.second, shift+1); 37 } 38 } 39 int main() 40 { 41 ios::sync_with_stdio(false); 42 cin.tie(0); 43 int root = sz++; 44 int n; 45 cin >> n; 46 for (int i = 1; i <= n; ++i) 47 { 48 string line; 49 cin >> line; 50 int last = 0; 51 int cur = 0; 52 for (int i = 0; i <= line.size(); ++i) 53 { 54 if (line[i] == '\\' || line[i] == 0) 55 { 56 string name = line.substr(last, i-last); 57 last = i+1; 58 if (!g[cur].count(name)) 59 { 60 g[cur][name] = sz++; 61 } 62 cur = g[cur][name]; 63 } 64 } 65 } 66 dfs(0, 0); 67 }