Yandex Algorithm 2018 Qualification
A. Lottery
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 = 32; 28 bool vis[maxn+1] = {0}; 29 30 int main() 31 { 32 int a = 10, b = 6; 33 for (int i = 0; i < a; ++i) 34 { 35 int x; 36 scanf("%d", &x); 37 vis[x] = true; 38 } 39 int T; 40 scanf("%d", &T); 41 for (int kase = 1; kase <= T; ++kase) 42 { 43 int cnt = 0; 44 for (int i = 0; i < b; ++i) 45 { 46 int x; 47 scanf("%d", &x); 48 cnt += vis[x]; 49 } 50 puts(cnt>=3?"Lucky":"Unlucky"); 51 } 52 }
B. Permutation Recovery
题意:对于一个n*n的permutation,n<=100,可以看作一个n*n的方阵。现将每一行看作一个长度为n的数列,每一列同理,n行和n列放在一起,打乱顺序。让你找出一组可能的permutation。保证输入有解。
观察:permutation第一个元素会作为开头出现两次,其他元素至多作为开头出现一次。所以找到开头出现两次的元素,把其中一个数列当作第一列,然后将每一列开头元素对应的数列输出即可。
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 = 100+1; 28 int n; 29 vector<int> g[maxn*maxn]; 30 vector<int> tar; 31 int main() 32 { 33 scanf("%d", &n); 34 for (int i = 1; i <= 2*n; ++i) 35 { 36 vector<int> tmp(n); 37 for (auto &e : tmp) 38 scanf("%d", &e); 39 if (g[tmp.front()].empty()) 40 g[tmp.front()] = tmp; 41 else 42 tar = tmp; 43 } 44 bool first = true; 45 for (auto e : tar) 46 { 47 const auto &v = g[e]; 48 for (auto e : v) 49 { 50 if (first) 51 first = false; 52 else 53 printf(" "); 54 printf("%d", e); 55 } 56 } 57 if (!first) 58 puts(""); 59 }
WA:数组开小了。
C. Beautiful Tables
题意:给你一个n,不超过1000,让你在n*n的方阵中填1-n的数字,使得每一行的元素和 and 每一列的元素和都能被n整除。问你有多少种填的方案,答案mod 1e9+7。
观察:对于左上角(n-1)*(n-1)的子方阵,随意填数,剩下最后一行和最后一列的数字就会被唯一确定,而且有解。所以答案就是n^[(n-1)*(n-1)]。
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 mod = 1e9+7; 28 ll power(ll base, ll p) 29 { 30 ll ret = 1; 31 while (p) 32 { 33 if (p&1) 34 ret = ret * base % mod; 35 p >>= 1; 36 base = base * base % mod; 37 } 38 return ret; 39 } 40 int main() 41 { 42 int n; 43 scanf("%d", &n); 44 printf("%lld\n", power(n, (n-1)*(n-1))); 45 }
D. Triangle Construction
题意:给你n (n <= 3e5) 个长度不超过1e18的木棍,长度计作L[1-n]。有q (q <= 3e5) 组询问,每组询问形如(l, r), 1 <= l <= r <= n。让你找出[l, r]中三个下标不同的木棍,使得这三个木根可以构成一个三角形。如果找不到输出-1。
观察:三条木棍能构成三角形,当且仅当最长边长度严格小于其余两条边长度和。可以想到,如果有一些木棍,找出一组可行解很简单,只需将木棍按照长度排序,然后从小到大找连续的三根木棍检查是否符合条件即可。如果找不到,便无解。
为什么这样是对的。可以考虑枚举最长边,那么为了尽可能达到条件,剩下较短的两条边要选尽量长的,所以只需要考虑相邻的三根木棍就好了。
下面考虑一下无解的情况,把长度排序,最坏的情况肯定是 1, 1, 2, 3, 5, 8, ....,即Fibonacci 数列。但是Fibonacci数列使呈指数级别增长的,算一下发现f[1] = f[2] = 1的Fibonacci数列第88项刚好超过1e18。所以如果木棍个数不小于88,那么一定可以找到解。
所以对于每个询问就只用考虑min(88, r-l+1)个元素,排序后检查是否有解。这样不用预处理,令len = min(88, r-l+1),单次询问时间为O(len*log(len))。
当然也可以先对每个位置l,暴力计算出使得[l, r]有解的最小的r,记作ans[l]。令len = min(88, n-l+1),这样处理一个l的复杂度是O(len*log(len)),整体处理所有l的话,令len = min(88, n),可以做到O(n*len*log(len))。单次询问O(1),例如询问[l, r]只需要看r和ans[l]的关系即可。如果r < ans[l],无解,否则直接输出答案即可。
考虑了一下是否可以将上面这种方法利用滑动窗口之类的优化,但是好像不能避开最坏情况。
code:
无预处理,单次询问时间为O(len*log(len))。
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 28 /* 29 by skydog 30 */ 31 #include <iostream> 32 #include <cstdio> 33 #include <vector> 34 #include <utility> 35 #include <algorithm> 36 #include <cmath> 37 #include <cstring> 38 #include <map> 39 #include <set> 40 #include <stack> 41 #include <queue> 42 #include <deque> 43 #include <cassert> 44 #include <list> 45 using namespace std; 46 typedef long long ll; 47 typedef pair<int, int> ii; 48 typedef pair<ll, ll> l4; 49 50 #define mp make_pair 51 #define pb push_back 52 #define db(x) cerr << #x << " = " << x << endl 53 54 const int maxn = 3e5+1; 55 ll len[maxn]; 56 int maxl = 88; 57 int n, q; 58 void solve(int l, int r) 59 { 60 vector<l4> v; 61 for (int i = l; i <= min(r, l+maxl-1); ++i) 62 v.push_back(mp(len[i], i)); 63 sort(v.begin(), v.end()); 64 for (int i = 2; i < v.size(); ++i) 65 if (v[i-2].first + v[i-1].first > v[i].first) 66 { 67 printf("%lld %lld %lld\n", v[i-2].second, v[i-1].second, v[i].second); 68 return; 69 } 70 puts("-1"); 71 } 72 const ll inf = 1000000000000000000; 73 int get_maxl() 74 { 75 ll a = 1, b = 1; 76 int cnt = 2; 77 while (b <= inf) 78 { 79 a += b; 80 swap(a, b); 81 ++cnt; 82 } 83 return cnt; 84 } 85 int main() 86 { 87 cerr << "maxl = " << (maxl = get_maxl()) << endl; 88 scanf("%d %d", &n, &q); 89 for (int i = 1; i <= n; ++i) 90 scanf("%lld", len+i); 91 for (int i = 1; i <= q; ++i) 92 { 93 int l, r; 94 scanf("%d %d", &l, &r); 95 solve(l, r); 96 } 97 98 }
O(n*len*log(len))预处理,O(1) 询问。
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 28 /* 29 by skydog 30 */ 31 #include <iostream> 32 #include <cstdio> 33 #include <vector> 34 #include <utility> 35 #include <algorithm> 36 #include <cmath> 37 #include <cstring> 38 #include <map> 39 #include <set> 40 #include <stack> 41 #include <queue> 42 #include <deque> 43 #include <cassert> 44 #include <list> 45 using namespace std; 46 typedef long long ll; 47 typedef pair<int, int> ii; 48 typedef pair<ll, ll> l4; 49 50 #define mp make_pair 51 #define pb push_back 52 #define db(x) cerr << #x << " = " << x << endl 53 54 const int maxn = 3e5+1; 55 ll len[maxn]; 56 int ans[maxn]; 57 int ret[maxn][3]; 58 int maxl = 88; 59 int n, q; 60 61 const ll inf = 1000000000000000000; 62 int get_maxl() 63 { 64 ll a = 1, b = 1; 65 int cnt = 2; 66 while (b <= inf) 67 { 68 a += b; 69 swap(a, b); 70 ++cnt; 71 } 72 return cnt; 73 } 74 multiset<l4> st; 75 bool check(int from, int to, multiset<l4>::iterator a, multiset<l4>::iterator b, multiset<l4>::iterator c) 76 { 77 if (a->first + b->first > c->first) 78 { 79 ans[from] = to; 80 ret[from][0] = a->second, ret[from][1] = b->second, ret[from][2] = c->second; 81 return true; 82 } 83 return false; 84 } 85 void solve(int i) 86 { 87 st.clear(); 88 int lim = min(n, i+maxl-1); 89 ans[i] = n+1; 90 for (int j = i; j <= lim; ++j) 91 { 92 st.insert(mp(len[j], j)); 93 multiset<l4>::iterator it = st.find(mp(len[j], j)), it1, it2, it_1, it_2; 94 it1 = it2 = it_1 = it_2 = st.end(); 95 if (it != st.end()) 96 { 97 it1 = it; 98 ++it1; 99 if (it1 != st.end()) 100 { 101 it2 = it1; 102 ++it2; 103 } 104 } 105 if (it != st.begin()) 106 { 107 it_1 = it; 108 --it_1; 109 if (it_1 != st.begin()) 110 { 111 it_2 = it_1; 112 --it_2; 113 } 114 } 115 if (it_1 != st.end()) 116 { 117 if (it_2 != st.end() && check(i, j, it_2, it_1, it)) 118 return; 119 if (it1 != st.end() && check(i, j, it_1, it, it1)) 120 return; 121 } 122 if (it1 != st.end() && it2 != st.end() && check(i, j, it, it1, it2)) 123 return; 124 } 125 } 126 int main() 127 { 128 cerr << "maxl = " << (maxl = get_maxl()) << endl; 129 scanf("%d %d", &n, &q); 130 for (int i = 1; i <= n; ++i) 131 scanf("%lld", len+i); 132 int nxt = 1; 133 for (int i = 1; i <= n; ++i) 134 solve(i); 135 for (int i = 1; i <= q; ++i) 136 { 137 int l, r; 138 scanf("%d %d", &l, &r); 139 if (r >= ans[l]) 140 { 141 printf("%d %d %d\n", ret[l][0], ret[l][1], ret[l][2]); 142 } 143 else 144 { 145 puts("-1"); 146 } 147 } 148 149 }
E.
题意:给你两个string的序列A[1-n], B[1-k],1 <= n, k <= 1e5,每个字符串长度都不超过10,B中的字符串没有重复。问有多少个(l, r),1 <= l <= r <= n满足B中的每一个元素都在A[l-r]中出现过,即set{A[l-r]} 包含 set{B[1-k]}。
观察:其实就是一个滑动窗口问题
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+1; 28 string s[maxn]; 29 int idx[maxn]; 30 map<string, int> id; 31 int cnt[maxn] = {0}, tot = 0; 32 int n, k; 33 int main() 34 { 35 ios::sync_with_stdio(false); 36 cin.tie(0); 37 cin >> n >> k; 38 for (int i = 1; i <= n; ++i) 39 cin >> s[i]; 40 for (int i = 1; i <= k; ++i) 41 { 42 string str; 43 cin >> str; 44 id[str] = i; 45 } 46 for (int i = 1; i <= n; ++i) 47 idx[i] = id[s[i]]; 48 ll ans = 0, nxt = 1; 49 cnt[0] = 1; 50 for (int i = 1; i <= n; ++i) 51 { 52 while (nxt <= n && tot < k) 53 { 54 if (cnt[idx[nxt]] == 0) 55 ++tot; 56 ++cnt[idx[nxt]]; 57 ++nxt; 58 } 59 if (tot < k) 60 break; 61 ans += n-nxt+2; 62 --cnt[idx[i]]; 63 if (cnt[idx[i]] == 0) 64 --tot; 65 } 66 printf("%lld\n", ans); 67 68 }
F.
题意:给你一颗大小不超过2e5的树,让你在树上选两个不同的点a和b。树上两点间的距离定义为两点间最短路径上的边数,树上一点的不稳定性定义为这个点到a或b距离的最小值,整棵树的不稳定性定义为,树上任意一点不稳定性的最大值。让你选出a,b使得树的不稳定性最小。
观察:如果选一个点的话会选在哪呢?直径中点。那两个点是不是也会和直径有关?令直径上点的个数为len,那么最小的不稳定性不会超过len/2,因为你只要在直径的中点上放一个点(如果直径中点在一条边上,就在这条边两个端点上均放一个点),那么其他点到这个点(或两个点)的最小距离一定不超过len/2。那么其实可以考虑最小的不稳定性mini,mini <= len/2。如果答案是mini,那么我们只需要选离直径上距直径两端点距离为mini的点就好了(如果有重复,选另外的点)。所以可以考虑二分答案。
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 = 2e5+1; 28 vector<int> g[maxn]; 29 int n, pre[maxn]; 30 int bfs(int cur) 31 { 32 int ret = -1; 33 memset(pre, -1, sizeof(pre)); 34 pre[cur] = 0; 35 queue<int> q; 36 q.push(cur); 37 while (!q.empty()) 38 { 39 int cur = q.front(); 40 q.pop(); 41 ret = cur; 42 for (auto nxt : g[cur]) 43 if (pre[nxt] == -1) 44 { 45 pre[nxt] = cur; 46 q.push(nxt); 47 } 48 } 49 assert(ret != -1); 50 return ret; 51 } 52 vector<int> diameter; 53 int dis[maxn]; 54 bool valid(int mid) 55 { 56 memset(dis, -1, sizeof(dis)); 57 queue<int> q; 58 int a = diameter[mid], b = diameter[diameter.size()-1-mid]; 59 dis[a] = dis[b] = 0; 60 q.push(a); 61 q.push(b); 62 while (!q.empty()) 63 { 64 int cur = q.front(); 65 q.pop(); 66 if (dis[cur] > mid) 67 return false; 68 int nxtd = dis[cur]+1; 69 for (auto nxt : g[cur]) 70 if (dis[nxt] == -1) 71 { 72 dis[nxt] = nxtd; 73 q.push(nxt); 74 } 75 } 76 return true; 77 } 78 int main() 79 { 80 scanf("%d", &n); 81 for (int i = 1; i <= n-1; ++i) 82 { 83 int u, v; 84 scanf("%d %d", &u, &v); 85 g[u].pb(v); 86 g[v].pb(u); 87 } 88 int lr = bfs(1); 89 int rr = bfs(lr); 90 //get diameter 91 { 92 int cur = rr; 93 while (cur) 94 { 95 diameter.pb(cur); 96 cur = pre[cur]; 97 } 98 assert(diameter.back() == lr); 99 } 100 int half_len = diameter.size()/2; 101 102 int l = 0, r = half_len, mid, ans=0; 103 while (l <= r) 104 { 105 mid = (l+r)>>1; 106 if (valid(mid)) 107 { 108 ans = mid; 109 r = mid-1; 110 } 111 else 112 l = mid+1; 113 } 114 int a = diameter[ans], b = diameter[diameter.size()-ans-1]; 115 if (a == b) 116 for (int i = 1; i <= n; ++i) 117 if (i != a) 118 { 119 b = i; 120 break; 121 } 122 printf("%d %d\n", a, b); 123 }