2017-2018 ACM-ICPC, NEERC, Northern Subregional Contest

哎,前方的路还很长。

 

A. Auxiliary Project

题意:考虑LED灯上显示0-9每个数字需要点亮的灯管数。给你一个n,不超过1e6,问你恰好点亮n个灯管可以点亮的数字的和,最大是多少

观察:n不是很大,可以背包。也可以找找规律,小规模打表,O(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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const int maxn = 1e6+1;
27 int f[maxn], n;
28 int c[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
29 int main()
30 {
31     freopen("auxiliary.in", "r", stdin);
32     freopen("auxiliary.out", "w", stdout);
33     scanf("%d", &n);
34     fill(f, f+1+n, -1e9);
35     f[0] = 0;
36     for (int i = 0; i < 10; ++i)
37         for (int j = c[i]; j <= n; ++j)
38             f[j] = max(f[j], f[j-c[i]]+i);
39     printf("%d\n", f[n]);
40 }
View 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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const int maxn = 1e6+1;
27 int f[maxn], n;
28 int c[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
29 int cc[3] = {1, 7, 4};
30 inline int solve(int a)
31 {
32     a -= 2;
33     return cc[a%3] + a/3*7;
34 }
35 int main()
36 {
37     freopen("auxiliary.in", "r", stdin);
38     freopen("auxiliary.out", "w", stdout);
39     scanf("%d", &n);
40     printf("%d\n", solve(n));
41 }
View Code

 

B. Boolean Satisfiability

题意:不超过26个boolean 变量,使用的符号只有变量名,“非”(negation)和 “或” (or)。问你有多少种赋值方式使得表达式为True。

观察:令出现的变量个数为cnt。如果一个变量x和非x同时出现,那么表达式一定为True,所有变量值随便取,答案是2^cnt。否则,至少有一个变量的值应该为True,答案是(2^cnt) - 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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 map<char, set<int> > ma;
27 const int maxn = 1e3+1;
28 char s[maxn];
29 int main()
30 {
31     freopen("boolean.in", "r", stdin);
32     freopen("boolean.out", "w", stdout);
33     scanf("%s", s);
34     int last = -1, ptr = 0, n = strlen(s);
35     while (ptr < n)
36     {
37         if (s[ptr] == '|');
38         else if (s[ptr] == '~')
39         {
40             ++ptr;
41             ma[s[ptr]].insert(1);
42         }
43         else
44         {
45             ma[s[ptr]].insert(0);
46         }
47         ++ptr;
48     }
49     for (const auto &e : ma)
50         if (e.second.size() > 1)
51         {
52             printf("%lld\n", 1ll<<ma.size());
53             return 0;
54         }
55     printf("%lld\n", -1 + (1ll<<ma.size()));
56     
57 }
View Code

 

C. Consonant Fencity

题意:给你一个长度不超过1e6的只有小写字母组成的字符串。定义aeiouwy这7个字母为元音,其余的字母为辅音。对于一个字符串,它的美丽度是相邻且大小写不同的辅音对的个数。允许你把一些字母变成大写(同一种字母大小写必须相同),让你输出美丽值最大的字符串。

观察:辅音有19个,可以暴力枚举19个大小写情况,每次检查有多少个美丽的辅音对。暴力枚举是2^19。但是因为大小写颠倒不影响答案,其实可以固定某一个辅音的大小写,枚举剩下18个的大小写,2^18。然后辅音对可以用一个int cnt[19][19]预处理出来,对于每一种大小写情况,19*19的暴力统计。

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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const string vowel = "aeiouwy";
27 int s[300];
28 char id[30];
29 int cnt[30][30];
30 char str[1000001];
31 int solve(int mask)
32 {
33     int ans = 0;
34     for (int i = 1; i <= s['z']; ++i)
35         for (int j = 1; j <= s['z']; ++j)
36             if (((mask>>i-1)&1)^((mask>>j-1)&1))
37             {
38                 //if (cnt[i][j]) cerr << id[i] << " " << id[j] << " " << cnt[i][j] << endl;
39                 ans += cnt[i][j];
40             }
41     return ans;
42 }
43 /*
44  16436
45  */
46 char perm[300];
47 int main()
48 {
49     freopen("consonant.in", "r", stdin);
50     freopen("consonant.out", "w", stdout);
51     for (char c = 'a'; c <= 'z'; ++c)
52         s[c] = 1;
53     for (auto c : vowel)
54         s[c] = 0;
55     int tot = 0;
56     for (char c = 'a'; c <= 'z'; ++c)
57         if (s[c])
58         {
59             s[c] = ++tot;
60         }
61     for (char c = 'a'; c <= 'z'; ++c)
62         if (s[c])
63             id[s[c]] = c;
64     scanf("%s", str);
65     int n = strlen(str);
66     for (int i = 0; i < n-1; ++i)
67         cnt[s[str[i]]][s[str[i+1]]] += 1;
68     int ans = 0, mask = 0;
69     for (int i = 0; i < (1<<18); ++i)
70     {
71         int tmp = solve(i);
72         if (tmp > ans)
73             ans = tmp, mask = i;
74     }
75     for (char c = 'a'; c <= 'z'; ++c)
76         perm[c] = c;
77     for (int i = 1; i <= s['z']; ++i)
78         if ((mask>>i-1)&1)
79             perm[id[i]] = id[i]+'Z'-'z';
80     for (int i = 0; i < n; ++i)
81         putchar(perm[str[i]]);
82     puts("");
83 
84 }
View Code

 

D. Dividing Marbles

E. Equal Numbers

题意:给你一个长度不超过3e5,元素都不超过1e6的正整数数列a[]。一次操作允许你将其中一个元素乘上任意一个正整数。对于k = 0,1,2, ..., n,让你输出进行k次操作,数列中不同数值个数的最小值。

观察:当k = n时,我们一定可以使所有元素相同,即把所有数都变成a[1] - a[n]的一个公倍数CM。所以操作可以简化成两类,一类是将某个a[i] 变成一个数列中存在的a[j],为了方便理解可以直接把a[j]选作数列中出现的a[i]倍数里最大的一个;或者把某个a[i]变成最终的CM。这样的话,也就可以把数列中初始的数分成两类,有后继的数(后继指数列中出现的自己的倍数)和没后继的数。注意,为了使不同数值的个数降低,我们需要把某一个数值的所有元素一起变换才会有效,所以要贪心的取出现次数最少的。计算答案的时候只需要考虑两种情况,就是CM已经出现,或者CM还未出现。

方法:为了图省事,写了前缀和加二分答案。其实是可以使用滑动窗口来做。

code:

下面第一种写法的意思是,要么不考虑生成CM。如果考虑生成CM,就一定要至少取一个没有后继的数。

 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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const int maxn = 1e6+1;
27 int cnt[maxn], n;
28 int main()
29 {
30     freopen("equal.in", "r", stdin);
31     freopen("equal.out", "w", stdout);
32     scanf("%d", &n);
33     for (int i = 0; i < n; ++i)
34     {
35         int x;
36         scanf("%d", &x);
37         ++cnt[x];
38     }
39     vector<int> a, b;
40     for (int i = 1; i < maxn; ++i)
41         if (cnt[i])
42         {
43             bool done = false;
44             for (int j = 2*i; j < maxn; j += i)
45                 if (cnt[j])
46                 {
47                     done = true;
48                     break;
49                 }
50             if (done)
51                 b.pb(cnt[i]);
52             else
53                 a.pb(cnt[i]);
54         }
55     int tot = a.size() + b.size();
56     swap(a.back(), *min_element(a.begin(), a.end()));
57     int mini = a.back();
58     a.pop_back();
59     b.pb(0);
60     a.insert(a.end(), b.begin(), b.end());
61     sort(a.begin(), a.end());
62     sort(b.begin(), b.end());
63     for (int i = 1;  i < a.size(); ++i)
64         a[i] += a[i-1];
65     for (int j = 1; j < b.size(); ++j)
66         b[j] += b[j-1];
67     for (int i = 0; i <= n; ++i)
68     {
69         int ans = tot - max(upper_bound(a.begin(), a.end(), i-mini)-a.begin()-1,(upper_bound(b.begin(), b.end(), i)-b.begin()-1));
70         printf("%d%c", ans, i==n?'\n':' ');
71     }
72 
73 }
View Code

 反思之后下面第二种写法会比较容易理解,即要么不考虑生成CM,要么生成CM,但是会浪费一次变换的机会(因为CM原本未在数列中出现,第一次把一类数变成CM不会使答案下降)。

 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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const int maxn = 1e6+1;
27 int cnt[maxn], n;
28 int main()
29 {
30     freopen("equal.in", "r", stdin);
31     freopen("equal.out", "w", stdout);
32     scanf("%d", &n);
33     for (int i = 0; i < n; ++i)
34     {
35         int x;
36         scanf("%d", &x);
37         ++cnt[x];
38     }
39     vector<int> a, b;
40     for (int i = 1; i < maxn; ++i)
41         if (cnt[i])
42         {
43             for (int j = 2*i; j < maxn; j += i)
44                 if (cnt[j])
45                 {
46                     b.pb(cnt[i]);
47                     break;
48                 }
49                 a.pb(cnt[i]);
50         }
51     int tot = a.size();
52     b.pb(0);
53     a.pb(0);
54     sort(a.begin(), a.end());
55     sort(b.begin(), b.end());
56     for (int i = 1;  i < a.size(); ++i)
57         a[i] += a[i-1];
58     for (int j = 1; j < b.size(); ++j)
59         b[j] += b[j-1];
60     for (int i = 0; i <= n; ++i)
61     {
62         int ans = tot - max(upper_bound(a.begin(), a.end(), i)-a.begin()-2,(upper_bound(b.begin(), b.end(), i)-b.begin()-1));
63         printf("%d%c", ans, i==n?'\n':' ');
64     }
65 
66 }
View Code

 

I. Intelligence in Perpendicularia

题意:给你一个不超过1000个点的简单多边形。每条边水平或者竖直。问你用不能从沿x轴或者y轴看到的边长有多少。

观察:其实就是多边形周长长减去最小外接长方形的周长。

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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 const int maxn = 1e3+1;
27 int x[maxn], y[maxn], n;
28 
29 int main()
30 {
31     freopen("intel.in", "r", stdin);
32     freopen("intel.out", "w", stdout);
33     scanf("%d", &n);
34     for (int i = 0; i < n; ++i)
35         scanf("%d %d", x+i, y+i);
36     int ans =  0;
37     for (int i = 0; i < n; ++i)
38         ans += abs(x[(i+1)%n]-x[i]) + abs(y[(i+1)%n]-y[i]);
39     ans -= 2*(*max_element(x, x+n)-*min_element(x, x+n) + *max_element(y, y+n)-*min_element(y, y+n));
40     printf("%d\n", ans);
41 }
View Code

 

K. Kotlin Island

题意:在一块h*w的土地上(h,w <=100),你可以横向或者纵向挖水沟。给你一个n,不超过1e9,让你输出一种使得水沟外,剩下陆地联通块个数为n的方案。

观察:对于一个纬度x,最多可以将其分成(x+1)/2条(切(x-1)/2刀)。所以可以如果可以把n分解成a*b的形式,且 min(a, b) <= (min(w, h)+1)/2, max(a, b) <= (max(w, h)+1)/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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 
27 const int maxn = 1e2+1;
28 char s[maxn][maxn];
29 int w, h, n;
30 void solve(int wl, int hl)
31 {
32     --wl, --hl;
33     for (int i = 1; i <= wl; ++i)
34         for (int j = 0; j < h; ++j)
35             s[2*i-1][j] = '#';
36     for (int j = 1; j <= hl; ++j)
37         for (int i = 0; i < w; ++i)
38             s[i][2*j-1] = '#';
39     for (int i = 0; i < w; ++i)
40         puts(s[i]);
41     exit(0);
42 }
43 int main()
44 {
45     freopen("kotlin.in", "r", stdin);
46     freopen("kotlin.out", "w", stdout);
47     scanf("%d %d %d", &w, &h, &n);
48     for (int i = 0; i < w; ++i)
49     {
50         fill(s[i], s[i]+h, '.');
51         s[i][h] = 0;
52     }
53     int wl = (w+1)/2, hl = (h+1)/2;
54     for (int i = 1; i <= wl; ++i)
55         if (n%i == 0 && n/i <= hl)
56             solve(i, n/i);
57     puts("Impossible");
58 }
View Code

 

L. Little Difference

题意:给你一个n,不超过1e18。让你把n分解成若干正因子的积,且任意两个因子间差不超过1。让你以任意顺序输出所有方案,如果有无限种方案,输出-1。

观察:如果n是2的自然数幂,那么n有无穷组分解,因为可以在分解中加任意个数的1。其他情况都有有限解。注意到{n}本身是一个解,然后如果分解成两个数,那么其中一定有一个是ceil(sqrt(n)),特判一下这种情况。最后如果解中的元素不少于3个,那么最小的元素肯定不超过1e6,暴力枚举每一个最小的元素即可。

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 typedef unsigned long long ull;
23 #define mp make_pair
24 #define pb push_back
25 
26 vector<vector<ll> > ans;
27 ll n;
28 void test(ll x, ll n)
29 {
30     if (x == 1 || n % x != 0)
31         return;
32     vector<ll> v;
33     while (n % x == 0)
34         n /= x, v.pb(x);
35     while (n % (x+1) == 0)
36         n /= x+1, v.pb(x+1);
37     if (n == 1)
38         ans.pb(v);
39 }
40 bool pow2(ll n)
41 {
42     while (n % 2 == 0)
43         n >>= 1;
44     return n == 1;
45 }
46 int main()
47 {
48     //freopen("little.in", "r", stdin);
49     //freopen("little.out", "w", stdout);
50     scanf("%lld", &n);
51     if (pow2(n))
52     {
53         puts("-1");
54         return 0;
55     }
56     ans.pb({n});
57     ll root = sqrt(n+0.5);
58     while (root*root <= n)
59         ++root;
60     while (root*root > n)
61         --root;
62     test(root, n);
63     for (ll i = 2; i*i*i <= n; ++i)
64         test(i, n);
65     printf("%d\n", ans.size());
66     for (const auto &v : ans)
67     {
68         printf("%d", v.size());
69         for (const auto e : v)
70             printf(" %lld", e);
71         putchar('\n');
72     }
73 }
View Code

 

posted @ 2018-02-11 01:18  大四开始ACM  阅读(215)  评论(0编辑  收藏  举报