2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) Solution

瞎扯

  又来打比赛了,发现自己菜得不行。

  果然我是标准的普及组选手。(这个队名是啥意思?我也不知道,因为不知道取啥队名,就随机在键盘上敲Emmm)

  这次队友很给力,把我不会的模拟和贪心全切掉了,并且每次翻译了正确的题意(我英语真垃圾)。(上次UESTC ACM Final的队友让我很绝望,既不会做题又给我翻译假题面)

  然后我把剩下的送分题都切掉了。

  于是被碾压了:

  开始通读全题(提供pdf真良心,避免网卡耽误时间),我觉得很绝望,尤其是那个B,读得我很绝望,直接扔翻译里都弃了。

  之后并不知道怎么做题,然后随机一道题,发现是水题是一道水题就直接切掉了。

  之后就开始看榜,那个题通过的队多就做哪个。

  佬表示这个不是正确的做法,正确的做法是每个人随机若干道题,先自己去做,不会的话然后扔给队友。

  挂掉的话也可以扔队友。

  但是我和我队友都挺菜的,这么搞可能完蛋了。

  下来发现B,J都是送分题。

Problem A Find a Number

题目大意

  问最小的满足各位数字之和为$s$并且能被$d$整除的正整数。

  首先用bfs确定它的位数,每个状态记录通过尽量小的转移边转移的前驱。

  然后做完了。

Code

 1 /**
 2  * Codeforces
 3  * Problem#1070A
 4  * Accepted
 5  * Time: 108ms
 6  * Memory: 24800k
 7  * Author: yyf
 8  */
 9 #include <iostream>
10 #include <cstdlib>
11 #include <cstring>
12 #include <cstdio>
13 #include <queue>
14 using namespace std;
15 typedef bool boolean;
16 
17 const int N = 5005, D = 505;
18 #define pii pair<int, int>
19 #define fi first
20 #define sc second
21 
22 int d, s;
23 int f[N][D];
24 char lst[N][D];
25 int lstr[N][D];
26 boolean vis[N][D];
27 
28 pii trans(int s, int r, int dig) {
29     return pii(s + dig, (r * 10 + dig) % d);
30 }
31 
32 inline void init() {
33     scanf("%d%d", &d, &s);
34 }
35 
36 queue<pii> que;
37 boolean bfs() {
38     que.push(pii(0, 0));
39     memset(f, 0x3f, sizeof(f));
40     f[0][0] = 0, vis[0][0] = true;
41     while (!que.empty()) {
42         pii e = que.front();
43         que.pop();
44 
45         for (int i = 0; i <= 9; i++) {
46             pii eu = trans(e.fi, e.sc, i);
47             if (eu.fi > s)
48                 break;
49             if (vis[eu.fi][eu.sc])
50                 continue;
51             vis[eu.fi][eu.sc] = true;
52             lst[eu.fi][eu.sc] = i + '0';
53             lstr[eu.fi][eu.sc] = e.sc;
54             f[eu.fi][eu.sc] = f[e.fi][e.sc] + 1;
55             que.push(eu);
56         }
57     }
58     return vis[s][0];
59 }
60 
61 void print(int s, int r) {
62     if (!s && !r)
63         return ;
64     int nr = lstr[s][r];
65     print(s - lst[s][r] + '0', nr);
66     putchar(lst[s][r]);
67 }
68 
69 
70 inline void solve() {
71     if (bfs()) {
72         print(s, 0);
73     } else
74         puts("-1");
75 }
76 
77 int main() {
78     init();
79     solve();
80     return 0;
81 }
Problem A

Problem B Berkomnadzor

题目大意

  给定一个IPv4白名单和黑名单,要求用最少的IPv4码给出一个包含所有黑名单中的地址但不包含任何一个白名单里的地址。

  先判掉无解的情况。

  剩下直接分治。

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070B
  4  * Accepted
  5  * Time: 530ms
  6  * Memory: 52800k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 #define ull unsigned long long
 13 #define pii pair<int, int>
 14 #define ll long long
 15 #define ui unsigned
 16 #define sc second
 17 #define fi first
 18 
 19 const signed ll llf = (signed ll) (~0ull >> 1);
 20 const signed int inf = (signed) (~0u >> 1);
 21 
 22 template <typename T>
 23 T __abs(T x) {
 24     return (x < 0) ? (-x) : (x);
 25 }
 26 
 27 template <typename T>
 28 void pfill(T* pst, const T* ped, T val) {
 29     for ( ; pst != ped; *(pst++) = val);
 30 }
 31 
 32 template <typename T>
 33 void pcopy(T* pst, const T* ped, T* pv) {
 34     for ( ; pst != ped; *(pst++) = *(pv++));
 35 }
 36 
 37 #define digit(_x) ((_x) >= '0' && (_x) <= '9')
 38 
 39 template <typename T>
 40 char* read(char* s, T& u) {
 41     for ( ; *s && !digit(*s); s++);
 42     if (!*s)
 43         return NULL;
 44     for (u = *s - '0'; ++s, digit(*s); u = u * 10 + *s - '0');
 45     return s;
 46 }
 47 
 48 const int N = 2e5 + 5;
 49 
 50 typedef class Segment {
 51     public:
 52         ui l, r;
 53         ui sgn;
 54 
 55         Segment() {    }
 56         Segment(ui l, ui r, ui sgn):l(l), r(r), sgn(sgn) {    }
 57 
 58         boolean operator < (Segment b) const { 
 59             if (l ^ b.l)
 60                 return l < b.l;
 61             return r < b.r;
 62         }
 63 
 64         boolean intersect(Segment b) {
 65             return !(b.r < l || b.l > r);
 66         }
 67 }Segment;
 68 
 69 int n;
 70 char buf[100];
 71 
 72 Segment read() {
 73     ui l, r, x;
 74     scanf("%s", buf);
 75     ui sgn = (buf[0] == '+');
 76     char* str = buf + 1;
 77     str = read(str, l);
 78     str = read(str, x), l = l << 8 | x;
 79     str = read(str, x), l = l << 8 | x;
 80     str = read(str, x), l = l << 8 | x;
 81     if (*str == '/') {
 82         read(str, x);
 83         x = 32 - x;
 84         l >>= x, l <<= x;
 85         if (x == 32)
 86             r = ~0u;
 87         else
 88             r = l | ((1 << x) - 1);
 89         return Segment(l, r, sgn);
 90     }
 91     return Segment(l, l, sgn);
 92 }
 93 
 94 int res = 0;
 95 vector<Segment> ss, rs;
 96 
 97 inline void init() {
 98     scanf("%d", &n);
 99     Segment s;
100     for (int i = 1; i <= n; i++)
101         s = read(), ss.push_back(s);
102 }
103 
104 void dividing(vector<Segment> &ss, ui l, ui r, ui bit) {
105     if (ss.empty())
106         return;
107     boolean app1 = false, app0 = false;
108     for (ui i = 0; i < ss.size(); i++)
109         app1 |= (ss[i].sgn == 1), app0 |= (ss[i].sgn == 0);
110     
111     if (!app1) {
112         rs.push_back(Segment(l, 32 - bit, 0));
113         return;
114     }
115 
116     if (!app0)
117         return;
118 
119     assert(l ^ r);
120 
121     ui mid = l + ((r - l) >> 1);
122     vector<Segment> ql, qr;
123     for (ui i = 0; i < ss.size(); i++) {
124         if (ss[i].r <= mid)
125             ql.push_back(ss[i]);
126         else if (ss[i].l > mid)
127             qr.push_back(ss[i]);
128         else {
129             ql.push_back(Segment(ss[i].l, mid, ss[i].sgn));
130             qr.push_back(Segment(mid + 1, ss[i].r, ss[i].sgn));
131         }
132     }
133     ss.clear();
134     dividing(ql, l, mid, bit - 1);
135     dividing(qr, mid + 1, r, bit - 1);
136 }
137 
138 inline void solve() {
139     sort(ss.begin(), ss.end());
140     for (ui i = 0, j; i < ss.size(); i = j)
141         for (j = i + 1; j < ss.size() && ss[j].intersect(ss[i]); j++)
142             if (ss[j].sgn ^ ss[i].sgn) {
143                 puts("-1");
144                 return;
145             }
146     dividing(ss, 0, ~0u, 32);
147     printf("%u\n", rs.size());
148     ui msk = (1 << 8) - 1;
149     for (ui i = 0, x; i < rs.size(); i++) {
150         x = rs[i].l;
151         printf("%u.%u.%u.%u/%u\n", x >> 24, x >> 16 & msk, x >> 8 & msk, x & msk, rs[i].r);
152     }
153 }
154 
155 int main() {
156     init();
157     solve();
158     return 0;
159 }
Problem B

Problem C Cloud Computing

题目大意

  一个公司每天需要$K$个CPU内核,有$m$个供应商,在第$l_i$到第$r_i$天,以每个CPU内核$p_i$的价格提供$c_i$个,如果一天买不够,那么必须把能够提供的内核都购买。问最小的总花费。

  随便拿个数据结构就过了。

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070C
  4  * Accepted
  5  * Time: 249ms
  6  * Memory: 28000k
  7  * Author: yyf
  8  */
  9 #include <algorithm>
 10 #include <iostream>
 11 #include <cstring>
 12 #include <cstdlib>
 13 #include <cstdio>
 14 #include <vector>
 15 using namespace std;
 16 typedef bool boolean;
 17 
 18 typedef class Opt {
 19     public:
 20         int i, p, c;
 21         int sgn;
 22 
 23         Opt(int i, int p, int c, int sgn):i(i), p(p), c(c), sgn(sgn) {    }
 24     
 25         boolean operator < (Opt b) const {
 26             return i < b.i;
 27         }
 28 }Opt;
 29 
 30 const int bzmax = 21;
 31 
 32 template <typename T>
 33 class IndexedTree {
 34     public:
 35         int s;
 36         T* ar;
 37 
 38         IndexedTree() {    }
 39         IndexedTree(int s):s(s) {
 40             ar = new T[(s + 1)];
 41             memset(ar, 0, sizeof(T) * (s + 1));
 42         }
 43 
 44         void add(int idx, T val) {
 45             for ( ; idx <= s; idx += (idx & (-idx)))
 46                 ar[idx] += val;
 47         }
 48 
 49         T query(int idx) {
 50             T rt = 0;
 51             for ( ; idx; idx -= (idx & (-idx)))
 52                 rt += ar[idx];
 53             return rt;
 54         }
 55 
 56         int kth(int k) {
 57             int rt = 0, cur = 0;
 58             for (int l = (1 << (bzmax - 1)); l; l >>= 1)
 59                 if ((rt | l) <= s && (cur + ar[rt | l]) < k)
 60                     rt |= l, cur += ar[rt];
 61             return rt + 1;
 62         }
 63 };
 64 
 65 #define ll long long
 66 
 67 const int V = 1e6 + 3;
 68 
 69 int n, K, m;
 70 IndexedTree<ll> itc;
 71 IndexedTree<ll> its;
 72 vector<Opt> vs;
 73 
 74 inline void init() {
 75     scanf("%d%d%d", &n, &K, &m);
 76     itc = IndexedTree<ll>(V);
 77     its = IndexedTree<ll>(V);
 78     for (int i = 1, l, r, c, p; i <= m; i++) {
 79         scanf("%d%d%d%d", &l, &r, &c, &p);
 80         vs.push_back(Opt(l, p, c, 1));
 81         vs.push_back(Opt(r + 1, p, c, -1));
 82     }
 83 }
 84 
 85 ll res = 0;
 86 inline void solve() {
 87     sort(vs.begin(), vs.end());
 88     int pv = 0, s = (signed) vs.size();
 89     for (int i = 1; i <= n; i++) {
 90         while (pv < s && vs[pv].i == i) {
 91             itc.add(vs[pv].p, vs[pv].c * vs[pv].sgn);
 92             its.add(vs[pv].p, vs[pv].c * 1ll * vs[pv].p * vs[pv].sgn);
 93             pv++;
 94         }
 95         ll cnt = itc.query(V);
 96         if (cnt < K) {
 97             res += its.query(V);
 98         } else {
 99             int p = itc.kth(K);
100             cnt = itc.query(p);
101             res += its.query(p) - (cnt - K) * p;
102         }
103     }
104     cout << res << endl;
105 }
106 
107 int main() {
108     init();
109     solve();
110     return 0;
111 }
Problem C

Problem D Garbage Disposal

题目大意

  每天会产生若干垃圾,每天的垃圾只能当天或者后天处理,第$n + 1$天不能留下垃圾。每次处理垃圾至多能处理$k$个单位,问最少的总处理次数。

  随便模拟一下就过了。

Code

 1 //Author: dream_maker
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 //----------------------------------------------
 5 //typename
 6 typedef long long ll;
 7 //convenient for
 8 #define fu(a, b, c) for (int a = b; a <= c; ++a)
 9 #define fd(a, b, c) for (int a = b; a >= c; --a)
10 #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
11 //inf of different typename
12 const int INF_of_int = 1e9;
13 const ll INF_of_ll = 1e18;
14 //fast read and write
15 template <typename T>
16 void Read(T &x) {
17   bool w = 1;x = 0;
18   char c = getchar();
19   while (!isdigit(c) && c != '-') c = getchar();
20   if (c == '-') w = 0, c = getchar();
21   while (isdigit(c)) {
22     x = (x<<1) + (x<<3) + c -'0';
23     c = getchar();
24   }
25   if (!w) x = -x;
26 }
27 template <typename T>
28 void Write(T x) {
29   if (x < 0) {
30     putchar('-');
31     x = -x; 
32   }
33   if (x > 9) Write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 //----------------------------------------------
37 const int N = 3e5 + 10;
38 ll a[N], n, k, ans = 0;
39 int main() {
40   Read(n), Read(k);
41   fu(i, 1, n) Read(a[i]);
42   fu(i, 1, n - 1) {
43     if (!a[i]) continue;
44     ll num = a[i] / k;
45     if (a[i] % k) ++num;
46     a[i + 1] = max(0ll, a[i + 1] - (num * k - a[i]));
47     ans += num;
48   }
49   if (a[n]) {
50     ans += a[n] / k;
51     if (a[n] % k) ++ans;
52   }
53   Write(ans);
54   return 0;
55 }
Problem D

Problem E Getting Deals Done

题目大意

  有$n$个任务和参数$m$,每个任务有一个耗时$p_i$,以及时限$t$,要求出$d$使得按照下面方式完成的任务尽量多。

  1. 按顺序完成$p_i \leqslant d$的任务
  2. 每做$m$个任务需要休息与做这$m$个任务花费的时间相等的时间。

  yangkai觉得这玩意儿可以三分,于是我们成功得到了-5.

  发现有用的$d$一定时某个$p_i$,可以把$p_i$排序,按顺序插进树状数组。

  每次可以二分一下求出能够完成的任务的数量。

  时间复杂度$O(n\log^{2} n)$。

  但是这个可以二分答案。

  判断条件是是否能够把所有可做的任务做完。

  答案只可能是它或者它加上1后的情况,

  因为再大的时候不会花更少的时间去做数量相同的任务。

  然后我的垃圾做法就被暴打了。

  然后注意几个地方:

  1. $p$相同的任务要一起加入树状数组
  2. 树状数组求第$k$大特判$k = 0$。

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070E
  4  * Accepted
  5  * Time: 608ms
  6  * Memory: 4700k
  7  */
  8 #include <algorithm>
  9 #include <iostream>
 10 #include <cstring>
 11 #include <cstdlib>
 12 #include <cstdio>
 13 #ifndef WIN32
 14 #define Auto "%lld"
 15 #else
 16 #define Auto "%I64d"
 17 #endif
 18 using namespace std;
 19 typedef bool boolean;
 20 #define ll long long
 21 
 22 template <typename T>
 23 class IndexedTree {
 24     public:
 25         int s;
 26         T* ar;
 27         
 28         IndexedTree() {    }
 29         IndexedTree(int s):s(s) {
 30             ar = new T[(s + 1)];
 31             memset(ar, 0, sizeof(T) * (s + 1));
 32         }
 33 
 34         void add(int idx, T val) {
 35             for ( ; idx <= s; idx += (idx & (-idx)))
 36                 ar[idx] += val;
 37         }
 38 
 39         T query(int idx) {
 40             T rt = 0;
 41             for ( ; idx; idx -= (idx & (-idx)))
 42                 rt += ar[idx];
 43             return rt;
 44         }
 45 
 46         int kth(int k) {
 47             if (!k)
 48                 return 0;
 49             int rt = 0, cur = 0;
 50             for (int i = (1 << 18); i; i >>= 1)
 51                 if ((rt | i) <= s && ar[rt | i] + cur < k)
 52                     rt |= i, cur += ar[rt];
 53             return rt + 1;
 54         }
 55 
 56         void clear() {
 57             s = 0;
 58             delete[] ar;
 59         }
 60 };
 61 
 62 const int N = 2e5 + 5;
 63 #define pii pair<int, int>
 64 #define fi first
 65 #define sc second
 66 
 67 int T;
 68 int n, m;
 69 ll t;
 70 IndexedTree<int> itc;
 71 IndexedTree<ll> its;
 72 int ps[N];
 73 pii ar[N];
 74 
 75 inline void init() {
 76     scanf("%d%d"Auto, &n, &m, &t);
 77     itc = IndexedTree<int>(n);
 78     its = IndexedTree<ll>(n);
 79     for (int i = 1; i <= n; i++)
 80         scanf("%d", ps + i), ar[i] = pii(ps[i], i);
 81 }
 82 
 83 int query(int i) {
 84     int l = 1, r = i, mid, k, ti;
 85     ll s = 0;
 86     while (l <= r) {
 87         mid = (l + r) >> 1;
 88         k = itc.kth(mid);
 89         s = its.query(k);
 90         ti = (mid - 1) / m * m;
 91         k = itc.kth(ti);
 92         s += its.query(k);
 93         if (s > t)
 94             r = mid - 1;
 95         else
 96             l = mid + 1;
 97     }
 98     return l - 1;
 99 }
100 
101 inline void solve() {
102     int ansc = 0, ansd = 1;
103     sort(ar + 1, ar + n + 1);
104     int i = 1, c;
105     while (i <= n) {
106         int cur = ar[i].fi;
107         while (i <= n && ar[i].fi == cur) {
108             itc.add(ar[i].sc, 1);
109             its.add(ar[i].sc, ar[i].fi);
110             i++;
111         }
112         c = query(i - 1);
113         if (c > ansc)
114             ansc = c, ansd = ar[i - 1].fi;
115     }
116     printf("%d %d\n", ansc, ansd);
117 }
118 
119 void clear() {
120     itc.clear();
121     its.clear();
122 }
123 
124 int main() {
125     scanf("%d", &T);
126     while (T--) {
127         init();
128         solve();
129         clear();
130     }
131     return 0;
132 }
Problem E

Problem F Debate

题目大意

  有$n$个观众,每个观众有一个观点是否支持$Alice$以及支持$Bob$,和一个影响度。

  要求选出一些人使得至少有一半的人支持$Alice$和一半的人支持$Bob$,最大化他们的影响度之和。

  两者都支持的一定全选。

  然后支持一方的配对选。

  剩下的再怎么选都不会改变还能选多少人,直接贪。

Code

  1 //Author: dream_maker
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 //----------------------------------------------
  5 //typename
  6 typedef long long ll;
  7 //convenient for
  8 #define fu(a, b, c) for (int a = b; a <= c; ++a)
  9 #define fd(a, b, c) for (int a = b; a >= c; --a)
 10 #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
 11 //inf of different typename
 12 const int INF_of_int = 1e9;
 13 const ll INF_of_ll = 1e18;
 14 //fast read and write
 15 template <typename T>
 16 void Read(T &x) {
 17   bool w = 1;x = 0;
 18   char c = getchar();
 19   while (!isdigit(c) && c != '-') c = getchar();
 20   if (c == '-') w = 0, c = getchar();
 21   while (isdigit(c)) {
 22     x = (x<<1) + (x<<3) + c -'0';
 23     c = getchar();
 24   }
 25   if (!w) x = -x;
 26 }
 27 template <typename T>
 28 void Write(T x) {
 29   if (x < 0) {
 30     putchar('-');
 31     x = -x; 
 32   }
 33   if (x > 9) Write(x / 10);
 34   putchar(x % 10 + '0');
 35 }
 36 //----------------------------------------------
 37 deque<ll> p[4];
 38 ll n, w, ans = 0;
 39 char s[3];
 40 bool cmp(ll a, ll b) {
 41   return a > b;
 42 }
 43 int main() {
 44   Read(n);
 45   fu(i, 1, n) {
 46     scanf("%s", s);
 47     Read(w);
 48     if (s[0] == '0') {
 49       if (s[1] == '0') p[0].push_back(w);
 50       else p[1].push_back(w);
 51     } else {
 52       if (s[1] == '0') p[2].push_back(w);
 53       else p[3].push_back(w);
 54     }
 55   }
 56   fu(i, 0, 3) sort(p[i].begin(), p[i].end(), cmp);
 57   ll ans = 0, num = 0, cnt0 = 0, cnt1 = 0;
 58   fv(i, p[3]) {
 59     ans += p[3][i];
 60     num++;
 61     cnt0++, cnt1++;
 62   }
 63   while (p[1].size() && p[2].size()) {
 64     num += 2;
 65     cnt1++, cnt0++;
 66     ans += p[1].front() + p[2].front();
 67     p[1].pop_front();
 68     p[2].pop_front();
 69   }
 70   int last = cnt1 * 2 - num;
 71   while (last--) {
 72     if (!p[0].size() && !p[1].size() && !p[2].size()) break;
 73     if (p[0].size()) {
 74       bool can = 1;
 75       if (p[1].size() && p[1].front() > p[0].front()) can = 0;
 76       if (p[2].size() && p[2].front() > p[0].front()) can = 0;
 77       if (can) {
 78         ans += p[0].front();
 79         p[0].pop_front();
 80         continue;
 81       }
 82     }
 83     if (p[1].size()) {
 84       bool can = 1;
 85       if (p[0].size() && p[0].front() > p[1].front()) can = 0;
 86       if (p[2].size() && p[2].front() > p[1].front()) can = 0;
 87       if (can) {
 88         ans += p[1].front();
 89         p[1].pop_front();
 90         continue;
 91       }
 92     }
 93     if (p[2].size()) {
 94       bool can = 1;
 95       if (p[1].size() && p[1].front() > p[2].front()) can = 0;
 96       if (p[0].size() && p[0].front() > p[2].front()) can = 0;
 97       if (can) {
 98         ans += p[2].front();
 99         p[2].pop_front();
100         continue;
101       }
102     }
103   }
104   /*if (p[1].size()) {
105     while (cnt1 * 2 > num && cnt0 * 2 > num) {
106       if (!p[1].size() && !p[0].size()) break;
107       if (!p[1].size() || !p[0].size()) {
108         if (!p[1].size()) {
109           num++;
110           ans += p[0].front();
111           p[0].pop_front();
112         } else {
113           num++, cnt0++;
114           ans += p[1].front();
115           p[1].pop_front();
116         }
117       }
118       if (p[0].front() > p[1].front()) {
119         num++;
120         ans += p[0].front();
121         p[0].pop_front();
122       } else {
123         num++, cnt0++;
124         ans += p[1].front();
125         p[1].pop_front();
126       }
127     }
128   } else if (p[2].size()) {
129     while (cnt1 * 2 > num && cnt0 * 2 > num) {
130       if (!p[2].size() && !p[0].size()) break;
131       if (!p[2].size() || !p[0].size()) {
132         if (!p[2].size()) {
133           num++;
134           ans += p[0].front();
135           p[0].pop_front();
136         } else {
137           num++, cnt1++;
138           ans += p[2].front();
139           p[2].pop_front();
140         }
141       }
142       if (p[0].front() > p[2].front()) {
143         num++;
144         ans += p[0].front();
145         p[0].pop_front();
146       } else {
147         num++, cnt1++;
148         ans += p[2].front();
149         p[2].pop_front();
150       }
151     }
152   }
153   while (p[0].size() && cnt1 * 2 > num && cnt0 * 2 > num) {
154     num++;
155     ans += p[0].front();
156     p[0].pop_front();
157   }*/
158   Write(ans);
159   return 0;
160 }
Problem F

Problem G Monsters and Potions

题目大意

  (请耐心读原题)

  暴力枚举拉力点,暴力枚举一下动的英雄。(能动就直接移动,不是爆搜)

  很好奇为什么只有100多个队过。

Code

  1 //Author: dream_maker
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 //----------------------------------------------
  5 //typename
  6 typedef long long ll;
  7 //convenient for
  8 #define fu(a, b, c) for (int a = b; a <= c; ++a)
  9 #define fd(a, b, c) for (int a = b; a >= c; --a)
 10 #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
 11 //inf of different typename
 12 const int INF_of_int = 1e9;
 13 const ll INF_of_ll = 1e18;
 14 //fast read and write
 15 template <typename T>
 16 void Read(T &x) {
 17   bool w = 1;x = 0;
 18   char c = getchar();
 19   while (!isdigit(c) && c != '-') c = getchar();
 20   if (c == '-') w = 0, c = getchar();
 21   while (isdigit(c)) {
 22     x = (x<<1) + (x<<3) + c -'0';
 23     c = getchar();
 24   }
 25   if (!w) x = -x;
 26 }
 27 template <typename T>
 28 void Write(T x) {
 29   if (x < 0) {
 30     putchar('-');
 31     x = -x; 
 32   }
 33   if (x > 9) Write(x / 10);
 34   putchar(x % 10 + '0');
 35 }
 36 //----------------------------------------------
 37 const int N = 1010;
 38 ll n, m;
 39 ll s[N], h[N], p[N];
 40 ll now[N], tp[N], tot;
 41 bool wk[N];
 42 void init() {
 43   fu(i, 1, n) now[i] = p[i];
 44   memset(tp, 0, sizeof(tp));
 45   memset(wk, 0, sizeof(wk));
 46   tot = 0;
 47 }
 48 bool check_walk(ll fro, ll to, ll vl) {
 49   if (fro == to) return 1;
 50   if (fro > to) {
 51     fd(i, fro, to) {
 52       if (now[i] < 0 && vl + now[i] < 0) return 0;
 53       vl += now[i];
 54     }
 55     return 1;
 56   } else {
 57     fu(i, fro, to) {
 58       if (now[i] < 0 && vl + now[i] < 0) return 0;
 59       vl += now[i];
 60     }
 61     return 1;
 62   }
 63 }
 64 void get_clean(ll l, ll r) {
 65   if (l >= r) {
 66     fd(i, l, r) now[i] = 0;
 67   } else {
 68     fu(i, l, r) now[i] = 0;
 69   }
 70 }
 71 struct Node {
 72   int id, dis;
 73 } w[N];
 74 bool cmp(Node a, Node b) {
 75   return a.dis < b.dis;
 76 }
 77 bool check(int pos) {
 78   init();
 79   fu(i, 1, m) {
 80     w[i].id = i;
 81     w[i].dis = labs(pos - s[i]);
 82   }
 83   sort(w + 1, w + m + 1, cmp);
 84   fu(i, 1, m) {
 85     if (check_walk(s[w[i].id], pos, h[w[i].id])) {
 86       tp[++tot] = w[i].id;
 87       get_clean(s[w[i].id], pos);
 88       wk[w[i].id] = 1;
 89     }
 90   }
 91   fu(i, 1, m) {
 92     if (!wk[w[i].id]) { 
 93       if (!check_walk(s[w[i].id], pos, h[w[i].id])) return 0;
 94       tp[++tot] = w[i].id;
 95       wk[w[i].id] = 1;
 96     }
 97   }
 98   Write(pos); putchar('\n');
 99   fu(i, 1, m) {
100     Write(tp[i]);
101     putchar(' ');
102   }
103   return 1;
104 }
105 int main() {
106   Read(n), Read(m);
107   fu(i, 1, m) Read(s[i]), Read(h[i]);
108   fu(i, 1, n) Read(p[i]);
109   fu(i, 1, n)
110     if (check(i)) return 0;
111   printf("-1");
112   return 0;
113 }
Problem G

Problem H BerOS File Suggestion

题目大意

  给定$n$个字符串,每次询问一个字符串,问有多少个字符串包含它作为子串,然后要求随便输出$n$个串中任意一个满足条件的串。

  居然有人写AC自动机。牛逼。

  我直接Hash。好像可以直接开一个string映射到xxx的map。

  感觉我对STL一无所知。

Code

 1 /**
 2  * Codeforces
 3  * Problem#1070E
 4  * Accepted
 5  * Time: 234ms
 6  * Memory: 15900k
 7  * Author: yyf
 8  */
 9 #include <iostream>
10 #include <cstdlib>
11 #include <cstdio>
12 #include <map>
13 using namespace std;
14 typedef bool boolean;
15 #define ull unsigned long long
16 
17 ull base = 233;
18 
19 const int N = 1e4 + 3;
20 
21 int n, q;
22 char strs[N][10];
23 map<ull, int> cnt;
24 map<ull, int> lst;
25 
26 inline void init() {
27     scanf("%d", &n);
28     for (int i = 1; i <= n; i++) {
29         scanf("%s", strs[i]);
30         char *s = strs[i];
31         for (int l = 0; s[l]; l++) {
32             ull ha = 0;
33             for (int r = l; s[r]; r++) {
34                 ha = ha * base + s[r];
35                 if (lst[ha] != i) {
36                     lst[ha] = i;
37                     cnt[ha]++;
38 //                    cerr << "added" << " " << ha << endl;
39                 }
40             }
41         }
42     }
43 }
44 
45 char str[10];
46 inline void solve() {
47     scanf("%d", &q);
48     while (q--) {
49         scanf("%s", str);
50         ull ha = 0;
51         for (int i = 0; str[i]; i++)
52             ha = ha * base + str[i];
53         int r1 = cnt[ha];
54         if (r1)
55             printf("%d %s\n", r1, strs[lst[ha]]);
56         else
57             puts("0 -");
58     }
59 }
60 
61 int main() {
62     init();
63     solve();
64     return 0;
65 }
Problem H

Problem I Privatization of Roads in Berland

题目大意

  给定一个$n$个点$m$条边的无向图,有足够多个公司,将每条边分配给一个公司,满足:

  1. 一个公司最多拥有两条边
  2. 每个顶点相邻的边至多被分给$K$个公司。

  容易发现,如果一个公司拥有了两条边,那么它存在一个公共顶点会比较优。

  考虑如果一个顶点的度数大于$K$,那么我们就要硬点它周边$2(d - k)$条边两两配对。

  然后每条边建一个点做匹配就完了。

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070I
  4  * Accepted
  5  * Time: 31ms
  6  * Memory: 200k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 #define ull unsigned long long
 13 #define pii pair<int, int>
 14 #define ll long long
 15 #define ui unsigned
 16 #define sc second
 17 #define fi first
 18 
 19 const signed ll llf = (signed ll) (~0ull >> 1);
 20 const signed int inf = (signed) (~0u >> 1);
 21 
 22 template <typename T>
 23 T __abs(T x) {
 24     return (x < 0) ? (-x) : (x);
 25 }
 26 
 27 template <typename T>
 28 void pfill(T* pst, const T* ped, T val) {
 29     for ( ; pst != ped; *(pst++) = val);
 30 }
 31 
 32 template <typename T>
 33 void pcopy(T* pst, const T* ped, T* pv) {
 34     for ( ; pst != ped; *(pst++) = *(pv++));
 35 }
 36 
 37 typedef class Edge {
 38     public:
 39         int ed, nx, cap, f;
 40 
 41         Edge() {    }
 42         Edge(int ed, int nx, int cap, int f):ed(ed), nx(nx), cap(cap), f(f) {    } 
 43 }Edge;
 44 
 45 typedef class MapManager {
 46     public:
 47         int *h;
 48         vector<Edge> es;
 49 
 50         MapManager() {    }
 51         MapManager(int n) {
 52              h = new int[(n + 1)];
 53              pfill(h, h + n + 1, -1);
 54         }
 55 
 56         void addEdge(int u, int v, int cap, int f) {
 57             es.push_back(Edge(v, h[u], cap, f));
 58             h[u] = (signed) es.size() - 1;
 59         }
 60 
 61         void addArc(int u, int v, int cap) {
 62             addEdge(u, v, cap, 0);
 63             addEdge(v, u, cap, cap);
 64         }
 65 
 66         Edge& operator [] (int p) {
 67             return es[p];
 68         }
 69 
 70         void clear() {
 71             delete[] h;
 72             es.clear();
 73         }
 74 }MapManager;
 75 
 76 const int N = 605;
 77 
 78 int n, m, K;
 79 int s = 0, t;
 80 int mxcap;
 81 int deg[N];
 82 MapManager g;
 83 
 84 inline void init() {
 85     scanf("%d%d%d", &n, &m, &K);
 86     pfill(deg, deg + n + 1, 0);
 87     g = MapManager(n + m + 1);
 88     for (int i = 1, u, v; i <= m; i++) {
 89         scanf("%d%d", &u, &v);
 90         deg[u]++, deg[v]++;
 91         g.addArc(0, i, 1);
 92         g.addArc(i, u + m, 1);
 93         g.addArc(i, v + m, 1);
 94     }
 95     t = n + m + 1, mxcap = 0;
 96     for (int i = 1; i <= n; i++)
 97         if (deg[i] > K)
 98             g.addArc(i + m, t, (deg[i] - K) << 1), mxcap += deg[i] - K;
 99     mxcap <<= 1;
100 }
101 
102 int cur[N << 1], _div[N << 1];
103 
104 int dfs(int p, int mf) {
105     if (p == t || !mf)
106         return mf;
107     int f, flow = 0;
108     for (int& i = cur[p], e; ~i; i = g[i].nx) {
109         e = g[i].ed;
110         if (g[i].f < g[i].cap && _div[e] == _div[p] + 1 && (f = dfs(e, min(mf, g[i].cap - g[i].f))) > 0) {
111             g[i].f += f;
112             g[i ^ 1].f -= f;
113             mf -= f;
114             flow += f;
115             if (!mf)
116                 break;
117         }
118     }
119     return flow;
120 }
121 
122 queue<int> que;
123 boolean bfs() {
124     pfill(_div, _div + t + 1, -1);
125     _div[s] = 0;
126     que.push(s);
127     while (!que.empty()) {
128         int e = que.front();
129         que.pop();
130         for (int i = g.h[e], eu; ~i; i = g[i].nx) {
131             eu = g[i].ed;
132             if (g[i].cap == g[i].f)
133                 continue;
134             if (~_div[eu])
135                 continue;
136             _div[eu] = _div[e] + 1;
137             que.push(eu);
138         }
139     }
140     return _div[t] != -1;
141 }
142 
143 int dinic() {
144     int rt = 0;
145     while (bfs()) {
146         pcopy(cur, cur + t + 1, g.h);
147         rt += dfs(s, inf);
148     }
149     return rt;
150 }
151 
152 int lab[N];
153 vector<int> mat[N];
154 
155 inline void solve() {
156     int flow = dinic(), used = 0;
157     
158     if (flow != mxcap) {
159         for (int i = 1; i <= m; i++)
160             printf("0 ");
161         putchar('\n');
162         return;
163     }
164 
165     for (int i = 1; i <= n; i++)
166         mat[i].clear();
167 
168     for (int i = 1; i <= m; i++)
169         for (int j = g.h[i]; ~j; j = g[j].nx)
170             if (g[j].cap == g[j].f && g[j].ed > m) {
171                 mat[g[j].ed - m].push_back(i);
172                 break;
173             }
174 
175     pfill(lab + 1, lab + m + 1, 0);
176     for (int i = 1, a, b; i <= n; i++)
177         while (!mat[i].empty()) {
178             a = mat[i].back();
179             mat[i].pop_back();
180             b = mat[i].back();
181             mat[i].pop_back();
182 
183             lab[a] = lab[b] = ++used;
184         }
185 
186     for (int i = 1; i <= m; i++) {
187         if (!lab[i])
188             lab[i] = ++used;
189         printf("%d ", lab[i]);
190     }
191     putchar('\n');
192 }
193 
194 inline void clear() {
195     g.clear();
196 }
197 
198 int T;
199 int main() {
200     scanf("%d", &T);
201     while (T--) {
202         init();
203         solve();
204         clear();
205     }
206     return 0;
207 }
Problem I

Problem J Streets and Avenues in Berhattan

题目大意

  给定一个长度为$K$的字母串,以及$n, m$,要求在$K$中选出不同位置的$n$个字符和$m$个字符,使得$n$个字符中和$m$字符中相同的对数尽量小。

  容易发现算贡献的只有一种字符。

  然后枚举它,随便dp一下就过了。

  时间复杂度$O(n|\Sigma|^2)$

  感觉用多项式除法可以去掉一个平方。

Code

 1 /**
 2  * Codeforces
 3  * Problem#1070J
 4  * Accepted
 5  * Time: 46ms
 6  * Memory: 1200k
 7  * Author: yyf
 8  */
 9 #include <bits/stdc++.h>
10 using namespace std;
11 typedef bool boolean;
12 
13 template <typename T>
14 void pfill(T* pst, const T* ped, T val) {
15     for ( ; pst != ped; *(pst++) = val);
16 }
17 
18 const int N = 2e5 + 5, Alpha = 27;
19 
20 int T;
21 int n, m, K;
22 int cnt[Alpha];
23 boolean f[N];
24 int g[N];
25 char str[N];
26 
27 inline void init() {
28     scanf("%d%d%d", &n, &m, &K);
29     scanf("%s", str);
30     pfill(cnt, cnt + Alpha, 0);
31     for (char *p = str; *p; p++)
32         cnt[*p - 'A']++;
33 }
34 
35 inline void solve() {
36     int res = (signed) (~0u >> 1);
37     for (int i = 0, used, diff; i < Alpha && res; i++) {
38         pfill(f, f + K + 1, false);
39         f[0] = true;
40         for (int c = 0, s = 0; c < Alpha; c++)
41             if (cnt[c] && (c ^ i)) {
42                 for (int j = s; ~j; j--)
43                     f[j + cnt[c]] |= f[j];
44                 s += cnt[c];
45             }
46 
47         for (int j = 0; j <= K; j++)
48             g[j] = (f[j] ? j : K + 1);
49         for (int j = K - 1; ~j; j--)
50             g[j] = min(g[j + 1], g[j]);
51 
52         for (int c = 0; c <= cnt[i] && c <= n && res; c++) {
53             used = g[n - c] + c;
54             if (K - used >= m) {
55                 diff = K - cnt[i] - g[n - c];
56                 if (diff >= m)
57                     res = 0;
58                 else
59                     res = min(res, (m - diff) * c);
60             }
61         }
62     }
63     printf("%d\n", res);
64 }
65 
66 int main() {
67     scanf("%d", &T);
68     while (T--) {
69         init();
70         solve();
71     }
72     return 0;
73 }
Problem J

Problem K Video Posts

题目大意

  (没听过)

  (听说是普及题)

Code

 1 //Author: dream_maker
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 //----------------------------------------------
 5 //typename
 6 typedef long long ll;
 7 //convenient for
 8 #define fu(a, b, c) for (int a = b; a <= c; ++a)
 9 #define fd(a, b, c) for (int a = b; a >= c; --a)
10 #define fv(a, b) for (int a = 0; a < (signed)b.size(); ++a)
11 //inf of different typename
12 const int INF_of_int = 1e9;
13 const ll INF_of_ll = 1e18;
14 //fast read and write
15 template <typename T>
16 void Read(T &x) {
17   bool w = 1;x = 0;
18   char c = getchar();
19   while (!isdigit(c) && c != '-') c = getchar();
20   if (c == '-') w = 0, c = getchar();
21   while (isdigit(c)) {
22     x = (x<<1) + (x<<3) + c -'0';
23     c = getchar();
24   }
25   if (!w) x = -x;
26 }
27 template <typename T>
28 void Write(T x) {
29   if (x < 0) {
30     putchar('-');
31     x = -x; 
32   }
33   if (x > 9) Write(x / 10);
34   putchar(x % 10 + '0');
35 }
36 //----------------------------------------------
37 const int N = 1e5 + 10;
38 ll n, k, a[N], ans[N];
39 int main() {
40   Read(n), Read(k);
41   fu(i, 1, n) Read(a[i]), a[i] += a[i - 1];
42   if (a[n] % k) {
43     printf("No");
44     return 0;
45   }
46   ll len = a[n] / k, last = 0, tot = 0;
47   fu(i, 1, n) {
48     if (a[i] - a[last] == len) {
49       ans[++tot] = i - last;
50       last = i;
51     } else if(a[i] - a[last] > len) {
52       printf("No");
53       return 0;
54     }
55   }
56   printf("Yes\n");
57   fu(i, 1, k) {
58     Write(ans[i]);
59     putchar(' ');
60   }
61   return 0;
62 }
Problem K

Problem L Odd Federalization

题目大意

  要求把$n$个点$m$条边分成$r$个部分,使得每一部分每个点的度都是偶数(不看不在同一部分内的边),问最小的$r$和方案。

  首先来猜想答案小于等于2.

  然后把每个点在哪个集合看成未知数$x_i$。

  用异或算一下每个点被割掉的边的奇偶性,然后就可以列方程了。

  高斯消元 + bitset就可以过了。

  证明可以见:http://codeforces.com/blog/entry/62570?#comment-465420 (orz Um_nik

  没怎么看懂。然后我来口胡两句。

  如果上面的方程存在解,答案一定小于等于2.否则必然存在若干行在模2意义下的向量加得到零向量,并且对应常数项加起来同于1。

  不妨设这些点是$1, 2, \dots, k$,对应的行向量是$\overrightarrow{x_{1}},\overrightarrow{x_2},\dots,\overrightarrow{x_k}$,这些行对应的常数项是$y_1, y_2, \dots, y_k$。

  由列方程的方法可以知道$y_i \equiv deg_i \pmod{2}$

  那么设$\sum_{i = 1}^{k} y_{i}\equiv \sum_{i = 1}^{k} deg_{i} = D \pmod{2}$,因为$\overrightarrow{x_{1}} + \overrightarrow{x_2} + \cdots + \overrightarrow{x_k} = \vec{0}$,所以当某个点的标号$a > k$时,那么$1$到$k$中的点与它相连的边的数量一定是偶数(不然这一维得到的和不是0),假设这一部分总边数是$E$,那么我们愉快地得到了$D\equiv D - E$。

  我们考虑$D - E$有着什么美妙的意义。我们考虑$G' = (V' = \left \{1, 2, \cdots, k\right \}, E')$这个诱导子图。$D - E$把满足$x \in V', y\not \in V'$的边$(x, y)$的贡献除去,因此$D - E$等于这个诱导子图中所有点的度数之和。它不可能是奇数。所以$D \equiv 0\pmod{2}$。

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070L
  4  * Accepted
  5  * Time: 93ms
  6  * Memory: 500k
  7  */
  8 #include <bits/stdc++.h>
  9 using namespace std;
 10 typedef bool boolean;
 11 
 12 #define ull unsigned long long
 13 #define pii pair<int, int>
 14 #define ll long long
 15 #define ui unsigned
 16 #define sc second
 17 #define fi first
 18 
 19 const signed ll llf = (signed ll) (~0ull >> 1);
 20 const signed int inf = (signed) (~0u >> 1);
 21 
 22 template <typename T>
 23 T __abs(T x) {
 24     return (x < 0) ? (-x) : (x);
 25 }
 26 
 27 template <typename T>
 28 void pfill(T* pst, const T* ped, T val) {
 29     for ( ; pst != ped; *(pst++) = val);
 30 }
 31 
 32 template <typename T>
 33 void pcopy(T* pst, const T* ped, T* pv) {
 34     for ( ; pst != ped; *(pst++) = *(pv++));
 35 }
 36 
 37 const int N = 2048;
 38 
 39 int n, m;
 40 boolean deg[N];
 41 bitset<N> eq[N];
 42 
 43 void reverse(bitset<N> &bs, int bit) {
 44     if (bs.test(bit))
 45         bs.reset(bit);
 46     else
 47         bs.set(bit);
 48 }
 49 
 50 inline void init() {
 51     scanf("%d%d", &n, &m);
 52     pfill(deg, deg + n, false);
 53     for (int i = 0; i < n; i++)
 54         eq[i].reset();
 55     for (int i = 1, u, v; i <= m; i++) {
 56         scanf("%d%d", &u, &v);
 57         u--, v--;
 58         eq[u].set(v);
 59         eq[v].set(u);
 60         reverse(eq[u], u);
 61         reverse(eq[v], v);
 62         deg[u] = !deg[u], deg[v] = !deg[v];
 63     }
 64 }
 65 
 66 void guass() {
 67     for (int i = 0; i < n; i++) {
 68         int p = -1;
 69         for (int j = i; j < n && p == -1; j++)
 70             if (eq[j].test(i))
 71                 p = j;
 72         if (p == -1)
 73             continue;
 74         swap(eq[p], eq[i]);
 75         swap(deg[p], deg[i]);
 76         for (int j = 0; j < n; j++)
 77             if ((j ^ i) && eq[j].test(i))
 78                 eq[j] ^= eq[i], deg[j] ^= deg[i];
 79     }
 80 }
 81 
 82 inline void solve() {
 83     guass();
 84     int r = 1;
 85     for (int i = 0; i < n && r == 1; i++)
 86         if (deg[i])
 87             r = 2;
 88     printf("%d\n", r);
 89     for (int i = 0; i < n; i++)
 90         printf("%d ", deg[i] + 1);
 91     puts("");
 92 }
 93 
 94 int T;
 95 int main() {
 96     scanf("%d", &T);
 97     while (T--) {
 98         init();
 99         solve();
100     }
101     return 0;
102 }
Problem L

Problem M Algoland and Berland

题目大意

  平面上有$n$个白点和$m$个黑点,满足任意三点不共线,要求给出一个生成树满足:

  • 每条边是平面上的一条线段,边与边只在端点处相交
  • 每条边恰好连接一个白点和一个黑点
  • 第$i$个黑点的度数为$r_i$,保证$r_1 + r_2 + \cdots + r_m = n + m - 1$

  神仙题。orz yjq_naiive & 他的数竞同学。

  设$r = \max\left \{ r_1, r_2, \dots, r_{m}\right \}$

  若$r = 1$,则$n + m = 2$,构造是显然的。

  若$r > 1$,考虑选出$r$最大的点$P$。

  考虑用一条经过$P$的直线将整个平面分成两个部分(先假设是可行的)。对于每部分我们都能够得到若干生成树,然后$P$向它们连边,这样就得到了解。

  如何保证这个能够得到若干个生成树,它的一个必要条件是$|E| - |V| \leqslant -1$。

  如何表示$|E| - |V|$?我们将每个点的赋一个权值$r_i - 1$(如果这个点是白点,那么它的$r_i$记为0),这样一部分内的点权和就是这一部分的$|E| - |V|$。(等价于把$|V|$的贡献摊在每个点上)

  当知道一部分$|E| - |V|$后可以计算出$P$需要向它们连的边数:$x = |V| - |E|$。

  我们知道$1\leqslant x \leqslant r - 1$,所以$1 - r \leqslant |V| - |E| \leqslant -1$

  然后我们把$P$当成两个点分别加入两个部分,这两半部分分别是一个子问题,可以递归处理。

  现在我们来证明一下必定存在这样一条直线满足条件。我们考虑随便找一条经过$P$的直线,记某一半的点权和为$s$,然后顺时针转动这条直线,每遇到一个点$s$至多会改变$r - 1$。如果存在一个不合法的时刻,那么必然满足$s < 1 - r$或者$s > -1$,考虑当直线旋转180度以后,这样$s' = r - s$,因为变化了至少$r$,所以必然存在某个时刻$1 - r \leqslant s \leqslant -1$。

  于是这道题愉快地被通过啦。时间复杂度$O(n^2\log n)$

Code

  1 /**
  2  * Codeforces
  3  * Problem#1070M
  4  * Accepted
  5  * Time: 624ms
  6  * Memory: 106100k
  7  */
  8 #include <algorithm>
  9 #include <iostream>
 10 #include <cassert>
 11 #include <cstdlib>
 12 #include <cstdio>
 13 #include <vector>
 14 #include <cmath>
 15 using namespace std;
 16 typedef bool boolean;
 17 
 18 template <typename T>
 19 void pfill(T* pst, const T* ped, T val) {
 20     for ( ; pst != ped; *(pst++) = val);
 21 }
 22 
 23 const double pi = acos(-1);
 24 const double eps = 0;
 25 
 26 typedef class Point {
 27     public:
 28         int x, y, deg, id;
 29 
 30         Point():x(0), y(0), deg(0), id(0)    {    }
 31         Point(Point p, int ndeg):x(p.x), y(p.y), deg(ndeg), id(p.id) {    }
 32         Point(int x, int y, int deg):x(x), y(y), deg(deg), id(0) {    }
 33 }Point;
 34 
 35 typedef class Event {
 36     public:
 37         int val, id;
 38         boolean add;
 39         double theta;
 40 
 41         Event() {}
 42         Event(int val, int id, boolean add, double theta):val(val), id(id), add(add), theta(theta) {    }
 43 
 44         boolean operator < (Event e) const {
 45             return theta < e.theta;
 46         }
 47 }Event;
 48 
 49 const int N = 6e3 + 6;
 50 
 51 int T;
 52 int n, m;
 53 int rs[N];
 54 int tp = 0;
 55 boolean onleft[N];
 56 Event events[N << 1];
 57 vector<Point> ps;
 58 
 59 inline void init() {
 60     scanf("%d%d", &n, &m);
 61     ps.clear();
 62     ps = vector<Point>(n + m);
 63     for (int i = 0; i < m; i++)
 64         scanf("%d", rs + i);
 65     for (int i = 0; i < n; i++)
 66         scanf("%d%d", &ps[i].x, &ps[i].y), ps[i].deg = 0, ps[i].id = i + 1;
 67     for (int i = 0; i < m; i++)
 68         scanf("%d%d", &ps[i + n].x, &ps[i + n].y), ps[i + n].deg = rs[i], ps[i + n].id = i + 1;
 69 }
 70 
 71 void solve(vector<Point> &ps) {
 72     int r = 0, mxid = -1;
 73     for (int i = 0; i < (signed) ps.size(); i++)
 74         if (ps[i].deg > r)
 75             r = ps[i].deg, mxid = i;
 76     if (r == 1) {
 77         int cnt = 0;
 78         for (int i = 0; i < (signed) ps.size(); i++)
 79             if (!ps[i].deg)
 80                 mxid = i, cnt++;
 81         assert(cnt == 1 || ps.size() == 1);
 82         for (int i = 0; i < (signed) ps.size(); i++)
 83             if (i ^ mxid)
 84                 printf("%d %d\n", ps[i].id, ps[mxid].id);
 85         return;
 86     }
 87     
 88     int dx, dy;
 89     double theta;
 90     tp = 0;
 91     int sum = 0;
 92     for (int i = 0; i < (signed) ps.size(); i++) {
 93         if (i == mxid)
 94             continue;
 95         dx = ps[i].x - ps[mxid].x, dy = ps[i].y - ps[mxid].y;
 96         theta = atan2(dy, dx);
 97         if (theta > 0 || fabs(theta) < eps) {
 98             sum += ps[i].deg - 1, onleft[i] = true;
 99             events[tp++] = Event(1 - ps[i].deg, i, false, theta);
100         } else
101             events[tp++] = Event(ps[i].deg - 1, i, true, theta + pi);
102     }
103     
104     sort(events, events + tp);
105     for (int i = 0; i < tp && (sum < 1 - r || sum > -1); i++) {
106         sum += events[i].val;
107         onleft[events[i].id] = events[i].add;
108     }
109 
110     assert(sum >= 1 - r && sum <= -1);
111 
112     vector<Point> pl, pr;
113     for (int i = 0; i < (signed) ps.size(); i++) {
114         if (i == mxid)
115             continue;
116         if (onleft[i])
117             pl.push_back(ps[i]);
118         else
119             pr.push_back(ps[i]);
120     }
121     pfill(onleft, onleft + ps.size(), false);
122     pl.push_back(Point(ps[mxid], -sum));
123     pr.push_back(Point(ps[mxid], r + sum));
124     ps.clear();
125 
126     solve(pl);
127     solve(pr);
128 }
129 
130 inline void solve() {
131     puts("YES");
132     solve(ps);
133 }
134 
135 int main() {
136     scanf("%d", &T);
137     while (T--) {
138         init();
139         solve();
140     }
141     return 0;
142 }
Problem M
posted @ 2018-10-21 22:32 阿波罗2003 阅读(...) 评论(...) 编辑 收藏