感觉这场难度迷茫,个人觉得难度排序为$A<B<D=E=G<C<F$
C题:
比赛结束1500+pp,结果出分900+fst,我就是fst的睿智Orz。
题意为给出$n,p,w,d$,求满足下列式子的任意$x,y,z$
$x*w+y*d=p\&\& x+y+z=n\&\&x\geq 0\&\&y\geq 0\&\&z\geq 0$
如果不看$z$,式子的前半段就是扩展欧几里得,所以先求出式子$x*w+y*d=p$的一种解$(x,y)$,然后再判断解的合法性,并将解转化成合法的。
由于求解时会爆longlong,所以用的java。
1 import java.math.BigInteger;
2 import java.util.*;
3 public class Main {
4 public static BigInteger x,y;
5 public static void main(String[] args) {
6 Scanner in = new Scanner(System.in);
7 BigInteger n,p,w,d,gcd,xx,yy;
8 n = in.nextBigInteger();
9 p = in.nextBigInteger();
10 w = in.nextBigInteger();
11 d = in.nextBigInteger();
12 gcd = exgcd(w,d);
13 if(p.mod(gcd)!=BigInteger.ZERO) {
14 System.out.printf("-1\n");
15 return ;
16 }
17 x=x.multiply(p.divide(gcd));
18 y=y.multiply(p.divide(gcd));
19 BigInteger lcm = w.divide(gcd).multiply(d);
20 xx = lcm.divide(w);
21 yy = lcm.divide(d);
22 if (x.compareTo(BigInteger.ZERO)==-1 &&y.compareTo(BigInteger.ZERO)==-1) {
23 System.out.printf("-1\n");
24 return ;
25 }
26 if (x.compareTo(BigInteger.ZERO)==-1) {
27 x=x.add (y.divide(yy).multiply(xx));
28 y=y.mod(yy);
29 }
30 else if (y.compareTo(BigInteger.ZERO)==-1) {
31 y=y.add (x.divide(xx).multiply(yy));
32 x=x.mod(xx);
33 }
34 if (x.add(y).compareTo(n)!=1 && x.compareTo(BigInteger.ZERO)!=-1 && y .compareTo(BigInteger.ZERO)!=-1) {
35 System.out.print(x);
36 System.out.print(" ");
37 System.out.print(y);
38 System.out.print(" ");
39 System.out.print(n.subtract(x.add(y)));
40 return ;
41 }
42 x=x.add (y.divide(yy).multiply(xx));
43 y=y.mod(yy);
44 if (x.add(y).compareTo(n)!=1 && x.compareTo(BigInteger.ZERO)!=-1 && y.compareTo(BigInteger.ZERO)!=-1&&x.multiply(w).add(y.multiply(d)).compareTo(p)==0) {
45 System.out.print(x);
46 System.out.print(" ");
47 System.out.print(y);
48 System.out.print(" ");
49 System.out.print(n.subtract(x.add(y)));
50 return ;
51 }
52 else {
53 System.out.printf("-1\n");
54 return ;
55 }
56 }
57 public static BigInteger exgcd(BigInteger a,BigInteger b) {
58 if (b == BigInteger.ZERO) {
59 x = BigInteger.ONE;
60 y = BigInteger.ZERO;
61 return a;
62 }
63 BigInteger g = exgcd(b, a.mod(b));
64 BigInteger t = x;
65 x = y;
66 y =t.subtract((a.divide(b)).multiply(y));
67 return g;
68 }
69 }
D题:
题意是给一棵树每个点染色,一共三种颜色,求染色花费最少并且相邻的三个点颜色不能重复。
因为相邻的三个点颜色不能一样,所以树上每个点的度都要$\leq 2$,即只有链才能染色。而链上染色只有$6$种方法,所以枚举$6$次即可。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int maxn = 2e5 + 10;
5 const ll inf = 1e18;
6 ll a[maxn][5], ans[maxn];
7 vector<int>p[maxn];
8 int cnt[10][3] = { {1,2,3},{2,1,3},{3,1,2},{1,3,2},{2,3,1},{3,2,1} };
9 ll dfs(int x, int fa, int w, int t) {
10 ll ans = a[x][cnt[w][t]];
11 for (int i = 0; i < p[x].size(); i++) {
12 int y = p[x][i];
13 if (y == fa)continue;
14 ans += dfs(y, x, w, (t + 1) % 3);
15 }
16 return ans;
17 }
18 void dfs1(int x, int fa, int w, int t) {
19 ans[x] = cnt[w][t];
20 for (int i = 0; i < p[x].size(); i++) {
21 int y = p[x][i];
22 if (y == fa)continue;
23 dfs1(y, x, w, (t + 1) % 3);
24 }
25 }
26 int main() {
27 int n;
28 scanf("%d", &n);
29 for (int j = 1; j <= 3; j++)
30 for (int i = 1; i <= n; i++)
31 scanf("%lld", &a[i][j]);
32 int f = 0, root = 1;
33 for (int i = 1, x, y; i < n; i++) {
34 scanf("%d%d", &x, &y);
35 p[x].push_back(y);
36 p[y].push_back(x);
37 if (p[x].size() > 2 || p[y].size() > 2)
38 f = 1;
39 }
40 for (int i = 1; i <= n; i++)
41 if (p[i].size() == 1)
42 root = i;
43 if (f == 1)
44 printf("-1\n");
45 else {
46 ll Min = inf, ansi = 0;
47 for (int i = 0; i < 6; i++) {
48 ll t = dfs(root, 0, i, 0);
49 if (Min > t)
50 Min = t, ansi = i;
51 }
52 printf("%lld\n", Min);
53 dfs1(root, 0, ansi, 0);
54 for (int i = 1; i <= n; i++)
55 printf("%lld%c", ans[i], i == n ? '\n' : ' ');
56 }
57 }
E题:
题意说有$n$个数,有一种操作可以让一个数$+1$或$-1$,问最多$k$次操作后$min(max(a_{i})-min(a_{i}))$的值。
排序后记录一下每种数的个数,然后从首尾扫,判断哪种数的个数少,然后就乱搞。感觉比之前的要简单...
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int maxn = 2e5 + 10;
5 ll a[maxn];
6 pair<ll, ll>b[maxn];
7 int main() {
8 ll n, k, cnt = 0, num = 0;
9 scanf("%lld%lld", &n, &k);
10 for (int i = 1; i <= n; i++)
11 scanf("%lld", &a[i]);
12 sort(a + 1, a + 1 + n);
13 for (int i = 1; i <= n + 1; i++) {
14 if (a[i - 1] && a[i] != a[i - 1])
15 b[++cnt] = pair<ll, ll>(a[i - 1], num), num = 0;
16 num++;
17 }
18 ll l = 1, r = cnt;
19 while (k && l != r) {
20 if (b[l].second <= b[r].second) {
21 int cg = min(b[l + 1].first - b[l].first, k / b[l].second);
22 if (cg == 0)break;
23 b[l].first += cg;
24 k -= b[l].second * cg;
25 if (b[l].first == b[l + 1].first)b[l + 1].second += b[l].second, l++;
26 }
27 else {
28 int cg = min(b[r].first - b[r-1].first, k / b[r].second);
29 if (cg == 0)break;
30 b[r].first -= cg;
31 k -= b[r].second * cg;
32 if (b[r].first == b[r-1].first)b[r - 1].second += b[r].second, r--;
33 }
34 }
35 printf("%lld\n", b[r].first - b[l].first);
36 }
F题:
题意是说一个有n个黑白点的环,操作k次,每次操作时对于每个点,点$i$的颜色变为$i,i-1,i+1$三个点颜色的众数,即为点$i-1$为黑,点$i+1$为黑,点$i$为白,则点$i$为黑。
大致画几个图就会发现,如果有两个即以上的相邻点同色,则块内的点的颜色永远都不会被改变。
所以我们只要找到每个点到达最终状态需要多少次就可以得到,将所有在块内的点初始为0,其余的状态扫正反扫两遍可以得到。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int maxn = 2e5 + 10;
5 char s[200010];
6 int vis[200010];
7 int main() {
8 memset(vis, 0x3f, sizeof(vis));
9 int n, k;
10 scanf("%d%d", &n, &k);
11 scanf("%s", s);
12 for (int i = 0; i < n; i++)
13 if (s[i] == s[(i + 1) % n] || s[i] == s[(i - 1 + n) % n])
14 vis[i] = 0;
15 for (int i = 2 * n - 1; i >= 0; i--)
16 vis[i % n] = min(vis[i % n], vis[(i + 1) % n] + 1);
17 for (int i = 0; i < 2 * n; i++)
18 vis[i % n] = min(vis[i % n], vis[(i - 1 + n) % n] + 1);
19 for (int i = 0; i < n; i++)
20 if (min(vis[i], k) % 2 == 1)
21 printf("%c", 'W' + 'B' - s[i]);
22 else
23 printf("%c", s[i]);
24 return 0;
25 }
G题:
题意是说有两个队列$q,p$,每个队列有$n$个人,编号为$1-n$,求最大的$ans=\sum_{i=1}^{n}max(p_{i},q_{i})\&\&ans\leq k$
先固定一个序列$q$,为$1,2,3\cdot \cdot \cdot n$,然后去构造序列$p$。
序列$p$初始也为$1,2,3\cdot \cdot \cdot n$,如果交换$p_{1},p_{n}$后的答案小于等于$k$,则交换,然后判断能否交换$p_{2},p_{n-1}$。
如果交换$p_{1},p_{n}$后的答案大于$k$,则直接找到位置$x$,使得交换$p_{1},p_{x}$后答案等于k。依次类推。
1 #include<bits/stdc++.h>
2 using namespace std;
3 typedef long long ll;
4 const int maxn = 2e6 + 10;
5 int ans[maxn];
6 int main() {
7 ll n, k;
8 scanf("%lld%lld", &n, &k);
9 ll sum = (n + 1) * n >> 1;
10 if (k < sum) {
11 printf("-1\n");
12 return 0;
13 }
14 for (int i = 1; i <= n; i++)
15 ans[i] = i;
16 int l = 1, r = n;
17 while (l < r) {
18 if (sum + r - l <= k) {
19 swap(ans[l], ans[r]);
20 sum += r - l; l++; r--;
21 }
22 else {
23 int p = k - sum + l;
24 swap(ans[l], ans[p]);
25 sum += p - l;
26 break;
27 }
28 }
29 printf("%lld\n", sum);
30 for (int i = 1; i <= n; i++)
31 printf("%d%c", i, i == n ? '\n' : ' ');
32 for (int i = 1; i <= n; i++)
33 printf("%d%c", ans[i], i == n ? '\n' : ' ');
34
35 }