NWERC 2017

A. Ascending Photo

题意:给你一个长度为n的整数序列a[1-n],n不超过1e6。让你把它切成最少的片段,使得切完之后重排片段可以使序列不严格递增。问最少切几段。

观察:

  首先可以离散化一下,把所有的值map到m个数[1,2,...,m],m <= n <= 1e6。然后我们可以线性扫一遍,把相邻且数字相同的位置连在一起形成连通分量,因为可以发现,连续的相同数字不需要被切开(如果有被切开,可以把切口平移至左端或者右端,并且不破坏答案成立)。

  然后我们就可以按照数值从小到大考虑每个联通分量前面切不切了。比如现在枚举到v,v对应的有一个区间[L1, R1]。我们看一下a[L1-1]是不是 v-1。如果a[L1-1] = v-1, 我们可以少切(a[L1-1], a[L1])这一刀。如果对于一个v,有多个区间满足上述条件,我们至多只能少切其中的一个。

  还有一个比较特殊的情况,就是,对于a[L1-1] = v-1,  我们想要少切(a[L1-1], a[L1])这一刀,但是a[L1-1]所对应的片段与其前面一个值为v-2的片段已经少切了一刀,我们要判断一下(a[L1-1], a[L1])可不可以切。比如 a[1-4] = [3 2 3 4],a[2]前面切一刀,然后a[3] = 3, a[2] = 2, 我们可以少切(a[2], a[3])这一刀,再看a[4]。虽然a[3] = 3, a[4] = 4,但是我们不能少切(a[3], a[4])这一刀,因为那样的话会形成(a[2] a[3] a[4]) = (2 3 4),剩下a[1]=3,没有办法重排。所以对于(a[2], a[3]),我们在决定要少切这刀的时候,发现这个v=3的片段不是唯一的(即外面还有别的3的片段),那么我们就要求这个片段之后v=4的片段不能少切。

  再考虑一个例子,a[1-5] = [2 3 4 2 3], 我们发现(a[1], a[2]) = (a[4], a[5]) = (2, 3),都可以少切一刀,该怎么选择呢?方法就是不用选择。一旦对于一种(v-1, v)有不少与2种合法的位置,那么就一定可以选择其中的位置少切一刀,使得对后面的操作没有影响。比如,a[1-5] = [2 3 4 2 3], 我们先少切(a[1], a[2])这一刀,后来发现(a[2], a[3])本来也可以少切的,那么我们一开始就不要少切(a[1] a[2]),反而去少切(a[4] a[5]), 这样就可以顺利的少切(a[2] a[3])了。

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 typedef unsigned long long ull;
28 const int maxn = 1e6+1;
29 int a[maxn], b[maxn], vis[maxn], n;
30 vector<ii> interval[maxn];
31 
32 int main()
33 {
34     scanf("%d", &n);
35     for (int i = 1; i <= n; ++i)
36         scanf("%d", a+i);
37     memcpy(b+1, a+1, n*sizeof(int));
38     sort(b+1, b+1+n);
39     int m = unique(b+1, b+1+n)-(b+1);
40     for (int i = 1; i <= n; ++i)
41         a[i] = lower_bound(b+1, b+1+m, a[i])-b;
42     a[0] = m+1;
43     for (int i = 1, nxt; i <= n; i = nxt)
44     {
45         nxt = i+1;
46         while (nxt <= n && a[nxt] == a[i])
47             ++nxt;
48         interval[a[i]].pb(mp(i, nxt-1));
49         i = nxt;
50     }
51     int ans = -1; //suppose we need to cut a[0] and a[1].
52     for (int i = 1; i <= n; ++i)
53     {
54         int cnt = 0, tail;
55         for (auto e : interval[i])
56         {
57             ++ans;
58             if (a[e.first-1] == i-1)
59             {
60                 if (vis[e.first-1]);
61                 else
62                 {
63                     ++cnt;
64                     tail = e.second;
65                 }
66             }
67         }
68         if (cnt == 0);
69         else
70         {
71             --ans;
72             if (interval[i].size() != 1 && cnt == 1)
73                 vis[tail] = true;
74         }
75     }
76     printf("%d
", ans);
77 }
78 // 12 1 2 2 3 3 1 2 3 4 1 2 3
View Code

WA:没有考虑到什么时候不能少切。

B. Boss Battls

题意:

观察:看样例猜出答案应该是max(1, n-2)。也可以考虑最坏情况,用环形[0, n-1]表示圆环,假设我们是在0的位置,为了堵住boss,我们沿着顺时针轰炸,且每次隔一格。然后最差情况boss起初在n-2的位置,和我们同方向运动。

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 main()
28 {
29     int n;
30     scanf("%d", &n);
31     printf("%d
", max(1, n-2));
32 }
View Code

WA:

C. Connect the Dots

题意:

观察:

code:

WA:

D. Dunglish

题意:

观察:

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 map<string, ll> ma[2];
28 map<string, string> translate;
29 vector<string> res;
30 inline int tag(const string&str)
31 {
32     return str[0] == 'c';
33 }
34 int main()
35 {
36     ios::sync_with_stdio(false);
37     cin.tie(0);
38     int n, m;
39     cin >> n;
40     res.resize(n);
41     for (auto &e : res)
42         cin >> e;
43     cin >> m;
44     for (int i = 0; i < m; ++i)
45     {
46         string a, b, c;
47         cin >> a >> b >> c;
48         translate[a] = b;
49         ma[tag(c)][a] += 1;
50     }
51     ll tot = 1, correct = 1;
52     for (const auto &e : res)
53     {
54         correct *= ma[1][e];
55         tot *= (ma[0][e] + ma[1][e]);
56     }
57     if (tot == 1)
58     {
59         for (int i = 0; i < n; ++i)
60             cout << translate[res[i]] << (i==n-1?'
':' ');
61         cout << (correct?"correct":"incorrect") << '
';
62     }
63     else
64     {
65         cout << correct << " correct
";
66         cout << tot-correct << " incorrect
";
67     }
68 }
View Code

WA:

E. English Restaurant

题意:

观察:

code:

WA:

F. Factor-Free Tree

题意:给你一个长度为n(不超过1e6)的整数数组a[1-n],1 <= a[i] <= 1e7。下面要求你建立一个二叉树,使得它的中序遍历是a[1-n],且每个点对应的值与它任意祖先对应的值都互质。如果不能,输出impossible。

观察:

  发现,对于一个位置i,设l[i] = max(j, where j < i && gcd(a[i], a[j]) != 1),r[i] = min(j, where i < j && gcd(a[i], a[j]) != 1)。那么i和l[i]间不能有祖先关系,i和r[i]同理。也就是说,对于一个区间[left, right],我们要找到一个i,使得l[i] < left && r[i] > right,让这个i作为整个区间对应树的根。而且可以想到,如果有多个选择,选哪一个都可以。

  有几个问题需要注意。

  第一个是如何快速的处理出l[i]和r[i]。可以想到,考虑gcd(a[i], a[j]) != 1,即不互质,只需要考虑a[i] 和 a[j] 的公共质因子就好了。2*3*5*7*11*13*17*19 = 9,699,690,所以对于任何一个a[i],不同的质因子个数至多只有8个。从左向右扫,对于每一个质数记录下来上一次包含这个质因子的数值出现的最右位置。每次处理一个l[i],只需要考虑max(occur[j], where j is a prime factor of a[i])。r[i]可以类似的从右向左扫来处理,当然也可以在处理a[i]的质因子j时,用i更新r[occur[j]]。

  第二个就是该如何记录下每个数的质因子了。这个需要离线预处理,想到可以用筛法。而怎样存下每个数的质因子呢,可以对于每个数v只存一个lp[v],即v的最小质因子,这个是可以在线性筛的时候搞出来的。然后对于每一个a[i],只需要不断查找lp[a[i]],并且继续处理a[i]/lp[a[i]]就好了。标程这样写过了。当然这样a[i] -> a[i]/lp[a[i]] -> (a[i]/lp[a[i]]) / lp[(a[i]/lp[a[i]])] -> ... -> 1是会经过log2(a[i])步,可以高达23步。但注意,我们可以在筛的途中,记录下来一个pre[v],表示v = pre[v] * (lp[v])^k  且 pre[v] mod lp[v] != 0。具体做法就是在lp[v] 和 lp[v/lp[v]]相同的时候,令pre[v] = pre[v/lp[v]],否则pre[v] = v/lp[v]。这样23步的过程可以简化成v -> pre[v] -> pre[pre[v]] -> ... -> 1,不超过8步。

  还有一点是少用max,很耗时。

code:


O(nlogn)筛法,记录maxp[v]

  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 = 1e7+1;
 28 const int maxn = 1e6+1;
 29 int n, a[maxn], p[N], pre[N];
 30 int l[maxn], r[maxn], occur[N];
 31 inline void Max(int &a, int b)
 32 {
 33     if (a < b) a = b;
 34 }
 35 inline void Min(int &a, int b)
 36 {
 37     if (a > b) a = b;
 38 }
 39 struct Interval
 40 {
 41     int l, r, p;
 42 };
 43 inline bool solve()
 44 {
 45     queue<Interval> q;
 46     q.push((Interval) {1, n, 0});
 47     while (!q.empty())
 48     {
 49         auto cur = q.front();
 50         q.pop();
 51         int tar = 0;
 52         for (int i = cur.l, j = cur.r; i <= j; ++i, --j)
 53         {
 54             if (l[i] < cur.l && r[i] > cur.r)
 55             {
 56                 tar = i;
 57                 break;
 58             }
 59             if (l[j] < cur.l && r[j] > cur.r)
 60             {
 61                 tar = j;
 62                 break;
 63             }
 64         }
 65         if (!tar)
 66             return false;
 67         p[tar] = cur.p;
 68         if (tar > cur.l)
 69             q.push((Interval) {cur.l, tar-1, tar});
 70         if (tar < cur.r)
 71             q.push((Interval) {tar+1, cur.r, tar});
 72     }
 73     return true;
 74 }
 75 int main()
 76 {
 77     // start init
 78     for (int i = 2; i < N; ++i)
 79         if (!p[i])
 80         {
 81             for (int j = i; j < N; j += i)
 82             {
 83                 p[j] = i;
 84                 pre[j] = p[j/i] == i? pre[j/i] : j/i;
 85             }
 86         }
 87     // end init
 88     scanf("%d", &n);
 89     for (int i = 1; i <= n; ++i)
 90         scanf("%d", a+i), r[i] = n+1;
 91     for (int i = 1; i <= n; ++i)
 92     {
 93         int cur = a[i];
 94         while (cur != 1)
 95         {
 96             int &prev = occur[p[cur]];
 97             if (prev)
 98             {
 99                 Max(l[i], prev);
100                 Min(r[prev], i);
101             }
102             prev = i;
103             cur = pre[cur];
104         }
105     }
106     
107     if (!solve())
108     {
109         puts("impossible");
110         exit(0);
111     }
112     for (int i = 1; i <= n; ++i)
113         printf("%d%c", p[i], i==n?'
':' ');
114 }
View Code

O(n)筛法

  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 = 1e7+1;
 28 const int maxn = 1e6+1;
 29 int n, a[maxn], p[N], pre[N], prime[N], psz;
 30 int l[maxn], r[maxn], occur[N];
 31 inline void Max(int &a, int b)
 32 {
 33     if (a < b) a = b;
 34 }
 35 inline void Min(int &a, int b)
 36 {
 37     if (a > b) a = b;
 38 }
 39 struct Interval
 40 {
 41     int l, r, p;
 42 };
 43 inline bool solve()
 44 {
 45     queue<Interval> q;
 46     q.push((Interval) {1, n, 0});
 47     while (!q.empty())
 48     {
 49         auto cur = q.front();
 50         q.pop();
 51         int tar = 0;
 52         for (int i = cur.l, j = cur.r; i <= j; ++i, --j)
 53         {
 54             if (l[i] < cur.l && r[i] > cur.r)
 55             {
 56                 tar = i;
 57                 break;
 58             }
 59             if (l[j] < cur.l && r[j] > cur.r)
 60             {
 61                 tar = j;
 62                 break;
 63             }
 64         }
 65         if (!tar)
 66             return false;
 67         p[tar] = cur.p;
 68         if (tar > cur.l)
 69             q.push((Interval) {cur.l, tar-1, tar});
 70         if (tar < cur.r)
 71             q.push((Interval) {tar+1, cur.r, tar});
 72     }
 73     return true;
 74 }
 75 int main()
 76 {
 77     // start init
 78     for (int i = 2; i < N; ++i)
 79     {
 80         if (!p[i])
 81         {
 82             prime[psz++] = i;
 83             p[i] = i;
 84             pre[i] = 1;
 85         }
 86         int k, j;
 87         for (j = 0; j < psz && prime[j] < p[i] && (k = prime[j] * i) < N; ++j)
 88         {
 89             pre[k] = i;
 90             p[k] = prime[j];
 91         }
 92         k = prime[j] * i;
 93         if (k < N)
 94         {
 95             pre[k] = pre[i];
 96             p[k] = prime[j];
 97         }
 98     }
 99     // end init
100     scanf("%d", &n);
101     for (int i = 1; i <= n; ++i)
102         scanf("%d", a+i), r[i] = n+1;
103     for (int i = 1; i <= n; ++i)
104     {
105         int cur = a[i];
106         while (cur != 1)
107         {
108             int &prev = occur[p[cur]];
109             if (prev)
110             {
111                 Max(l[i], prev);
112                 Min(r[prev], i);
113             }
114             prev = i;
115             cur = pre[cur];
116         }
117     }
118     
119     if (!solve())
120     {
121         puts("impossible");
122         exit(0);
123     }
124     for (int i = 1; i <= n; ++i)
125         printf("%d%c", p[i], i==n?'
':' ');
126 }
View Code

WA:

G. Glyph Recognition

题意:

观察:

code:

WA:

H. High Score

题意:

观察:

code:

WA:

I. Installing Apps

题意:

观察:

code:

WA:

J. Juggling Toupe

题意:

观察:

code:

WA:

K. Knockout Tourmanent

题意:

观察:

code:

WA:

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