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 }
View Code

 

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 }
View Code

 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 }
View Code

 

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 }
View Code

  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 }
View Code

 

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 }
View Code

 

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 }
View Code

 

posted @ 2018-02-19 20:40  大四开始ACM  阅读(526)  评论(0编辑  收藏  举报