Shoi2017试题泛做

Posted cot

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shoi2017试题泛做相关的知识,希望对你有一定的参考价值。

一口气做完六个省的省选(误)

Day1

[Shoi2017]期末考试

枚举最大的天数,然后代价贪心地O(1)计算。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 #define R register
 5 typedef long long ll;
 6 #define maxn 100010
 7 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
 8 #define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0)
 9 #define dmin(_a, _b) ((_a) < (_b) ? (_a) : (_b))
10 int a[maxn], b[maxn];
11 int main()
12 {
13     R ll A, B, C, sum = 0, suma = 0; scanf("%lld%lld%lld", &A, &B, &C);
14     R int n, m, maxx = 0; scanf("%d%d", &n, &m);
15     for (R int i = 1; i <= n; ++i) scanf("%d", &a[i]), suma += a[i];
16     for (R int i = 1; i <= m; ++i) scanf("%d", &b[i]), sum += b[i];
17     std::sort(a + 1, a + n + 1);
18     std::sort(b + 1, b + m + 1);
19     maxx = b[m];
20     R int p = m + 1, pp = n + 1;
21     R ll pre = sum, suf = 0, ans = 1e18;
22     for (R int i = maxx; i; --i)
23     {
24         while (p && b[p - 1] >= i) --p, suf += b[p], pre -= b[p];
25         while (pp && a[pp - 1] >= i) suma -= a[--pp];
26         R ll need = suf - 1ll * i * (m - p + 1);
27         if (A >= B)
28         {
29             R ll cost = (1ll * i * (pp - 1) - suma) * C + need * B;
30             cost > 0 ? cmin(ans, cost) : 0;
31         }
32         else
33         {
34             R ll v = 1ll * i * (p - 1) - pre;
35 //            printf("v = %lld %lld %d\\n", v, need, i);
36 //            printf("pp %d suma %lld\\n", pp, suma);
37             cmin(v, need);
38             R ll cost = (1ll * i * (pp - 1) - suma) * C + v * A;
39             need -= v;
40             cost += need * B;
41             cost > 0 ? cmin(ans, cost) : 0;
42 //            printf("%lld\\n", ans);
43         }
44     }
45     printf("%lld\\n", ans);
46     return 0;
47 }
D1T1

[Shoi2017]相逢是问候

恶心题。广义欧拉定理+线段树。

不懂什么是广义欧拉定理的同学(比如我)自行Baidu吧。大意和普通的欧拉定理是差不多的。

我们想象一下,指数第一次是模phi(p),第二次就是模phi(phi(p)),如此迭代下去直到模数变成1,那么就和原先的数无关了,并且因为后面都是模1,到最后的时候每次操作相当于最上面的那个c拿去模1,结果还是c^c^c...的多少次方,所以它的值没有变。

一直迭代phi的这个操作我们联想到了 2749: [HAOI2012]外星人 ,可以证明这个操作次数是log级别的(详见那题题解)。

根据以上预备知识我们可以得到以下算法:

用线段树维护区间没有变成自环的点的位置,然后每次找到这个点暴力重新计算数值。

复杂度是每个点最多算log次*每次要算log次*快速幂的log,所以是3个log的。(很爆炸的复杂度)

  1 #include <cstdio>
  2  
  3 #define R register
  4 #define maxn 50010
  5 #define maxh 25
  6 #define maxx 500000
  7 typedef long long ll;
  8 bool flag, fl[maxh][maxx + 10], vis[maxh][maxx + 10];
  9 int dp[maxh][maxx + 10];
 10 int ph[2333], phcnt, fc, p, c;
 11 int counter;
 12 inline int qpow(R int base, R int power, R int mh)
 13 {
 14     flag = 0;
 15     R bool memo = power <= maxx && phcnt - mh < maxh;
 16     if (memo && vis[phcnt - mh][power]) {flag = fl[phcnt - mh][power]; ++counter; return dp[phcnt - mh][power];}
 17     R int mod = ph[mh], tp = power;
 18 //    fprintf(stderr, "base %d %d\\n", mh, power);
 19     R int ret = 1; ret >= mod ? flag = 1, ret %= mod : 0;
 20     for (R ll t; power; power >>= 1, base = 1ll * base * base % mod)
 21         power & 1 ? (t = 1ll * ret * base) >= mod ? flag = 1, ret = t % mod : ret = t : 0;
 22 //    printf("flag %d\\n", flag);
 23     memo ? vis[phcnt - mh][tp] = 1, fl[phcnt - mh][tp] = flag, dp[phcnt - mh][tp] = ret : 0;
 24     return ret;
 25 }
 26 inline int phi(R int x)
 27 {
 28     R int t = x, ret = 1;
 29 //    printf("%d\\n", x);
 30     for (R int i = 2; 1ll * i * i <= x; ++i)
 31         if (t % i == 0)
 32         {
 33             t /= i; ret *= i - 1;
 34             while (t > 1 && t % i == 0)
 35                 t /= i, ret *= i;
 36         }
 37     t != 1 ? ret *= t - 1 : 0;
 38     return ret;
 39 }
 40 ll sum[maxn << 2];
 41 bool tr[maxn << 2];
 42 int cov[maxn], a[maxn], ql, qr, qv;
 43 inline void update(R int o)
 44 {
 45     sum[o] = (sum[o << 1] + sum[o << 1 | 1]) % p;
 46     tr[o] = tr[o << 1] && tr[o << 1 | 1];
 47 }
 48 void build(R int o, R int l, R int r)
 49 {
 50     if (l == r)
 51     {
 52         sum[o] = a[l];
 53         tr[o] = phcnt == 0;
 54         return ;
 55     }
 56     R int mid = l + r >> 1;
 57     build(o << 1, l, mid); build(o << 1 | 1, mid + 1, r);
 58     update(o);
 59 }
 60 int query(R int o, R int l, R int r)
 61 {
 62     if (ql <= l && r <= qr) return sum[o];
 63     R int mid = l + r >> 1, ret = 0;
 64     if (ql <= mid) (ret += query(o << 1, l, mid)) %= p;
 65     if (mid < qr) (ret += query(o << 1 | 1, mid + 1, r)) %= p;
 66     return ret;
 67 }
 68 void modify(R int o, R int l, R int r)
 69 {
 70     if (l == r)
 71     {
 72         if (tr[o]) return ;
 73         ++cov[l];
 74         a[l] >= ph[cov[l]] ? flag = 1 : flag = 0;
 75         R int ff = a[l] % ph[cov[l]];
 76 //        printf("apos %d flag %d\\n", a[l], flag);
 77         for (R int i = cov[l]; i; --i) ff = qpow(c, ff + (flag ? ph[i] : 0), i - 1);
 78  
 79 //        printf("l %d ff %d\\n", l, ff);
 80         tr[o] = cov[l] == phcnt;
 81         sum[o] = ff;
 82         return ;
 83     }
 84     R int mid = l + r >> 1;
 85     if (ql <= l && r <= qr)
 86     {
 87         if (!tr[o << 1]) modify(o << 1, l, mid);
 88         if (!tr[o << 1 | 1]) modify(o << 1 | 1, mid + 1, r);
 89     }
 90     else
 91     {
 92         if (ql <= mid) modify(o << 1, l, mid);
 93         if (mid < qr) modify(o << 1 | 1, mid + 1, r);
 94     }
 95     update(o);
 96 }
 97 int main()
 98 {
 99     R int n, m; scanf("%d%d%d%d", &n, &m, &p, &c);
100     ph[0] = p; for (; ph[phcnt] != 1;) ph[++phcnt] = phi(ph[phcnt - 1]);
101     ph[++phcnt] = 1;
102 //    fprintf(stderr, "phcnt %d\\n", phcnt);
103 //    for (R int i = phcnt - 1; i; --i) fc = qpow(c, fc + ph[i], ph[i - 1]);
104 //    printf("%d\\n", fc);
105     for (R int i = 1; i <= n; ++i) scanf("%d", &a[i]);
106     build(1, 1, n);
107     for (; m; --m)
108     {
109         R int opt, l, r; scanf("%d%d%d", &opt, &l, &r);
110         if (opt == 0)
111         {
112             ql = l; qr = r;
113             modify(1, 1, n);
114         }
115         else
116         {
117             ql = l; qr = r;
118             printf("%d\\n", query(1, 1, n));
119         }
120     }
121 //    fprintf(stderr, "%d\\n", counter);
122     return 0;
123 }
D1T2

[Shoi2017]组合数问题

循环矩阵快速幂,做法和 4818: [Sdoi2017]序列计数 有点像。

 1 #include <cstdio>
 2 #include <algorithm>
 3 #include <cstring>
 4 
 5 #define R register
 6 typedef long long ll;
 7 int k, p;
 8 typedef int Vector[60];
 9 Vector base, ans;
10 void mul(R Vector A, R Vector B)
11 {
12     R Vector C; memset(C, 0, k << 2);
13     for (R int i = 0; i < k; ++i)
14         for (R int j = 0; j < k; ++j)
15             C[(i + j) % k] = (C[(i + j) % k] + 1ll * A[i] * B[j]) % p;
16     memcpy(A, C, k << 2);
17 }
18 int main()
19 {
20     R int n, r; scanf("%d%d%d%d", &n, &p, &k, &r);
21     base[0] = 1; ++base[k - 1];
22     ans[0] = 1;
23     for (R ll power = 1ll * n * k; power; power >>= 1, mul(base, base))
24         power & 1 ? mul(ans, base), 1 : 0;
25 //    for (R int i = 0; i < k; ++i) printf("%d ", ans[i]);
26     printf("%d\\n", ans[r]);
27     return 0;
28 }
D1T3

Day2

 [Shoi2017]摧毁“树状图”

恶心题*2。树形DP

这题本来写了一个换根的做法结果因为太乱太复杂就推掉重新写了。(论想清楚再写的重要性)

最后参考了 SD_le 做法的题解。这位神犇的状态设计有理有据令人信服,所以我就参考了他的DP状态设计,然后自己推了一遍转移。然而推完以后还是不能1A,对拍以后才发现自己漏掉了好几种情况。。。(我好菜啊.jpg)

  1 #include <cstdio>
  2 #include <cstring>
  3 
  4 #define R register
  5 #define maxn 500010
  6 struct Edge {
  7     Edge *next;
  8     int to;
  9 } *last[maxn], e[maxn << 1], *ecnt = e;
 10 inline void link(R int a, R int b)
 11 {
 12     *++ecnt = (Edge) {last[a], b}; last[a] = ecnt;
 13     *++ecnt = (Edge) {last[b], a}; last[b] = ecnt;
 14 }
 15 int f1[maxn], f2[maxn], f3[maxn], f4[maxn], g1[maxn], g2[maxn], ans;
 16 #define cmax(_a, _b) (_a < (_b) ? _a = (_b) : 0)
 17 void dfs(R int x, R int fa)
 18 {
 19     R int tf1, tf2, tf3, tf4, tg1, tg2, deg = 0, mx = 0;
 20     R bool debug = x == 1;
 21     for (R Edge *iter = last[x]; iter; iter = iter -> next)
 22         if (iter -> to != fa)
 23         {
 24             dfs(iter -> to, x); ++deg;
 25             tf1 = f1[x]; tf2 = f2[x]; tf3 = f3[x]; tf4 = f4[x]; tg1 = g1[x]; tg2 = g2[x];
 26             
 27             cmax(tg1, f2[iter -> to]);
 28             cmax(tg1, g1[iter -> to] - 1);
 29             
 30             cmax(tg2, g1[x] + g1[iter -> to] - 1);
 31             cmax(tg2, g1[x] + f2[iter -> to]);
 32             cmax(tg2, f4[iter -> to]);
 33             cmax(tg2, g2[iter -> to] - 1);
 34 
 35             
 36             cmax(tf1, f1[iter -> to] - 1);
 37             
 38             cmax(tf2, f1[x] + f1[iter -> to] - 1);
 39             
 40             cmax(tf3, f1[x] + f2[iter -> to] - 1);// && debug ? printf("t1 %d %d\\n", iter -> to, tf3) : 0;
 41             cmax(tf3, f1[x] + g1[iter -> to] - 1);// && debug ? printf("t2 %d %d\\n", iter -> to, tf3) : 0;
 42             cmax(tf3, f2[x] + f1[iter -> to] - 1);// && debug ? printf("t3 %d %d\\n", iter -> to, tf3) : 0;
 43             cmax(tf3, mx + f1[iter -> to] - 2);// && debug ? printf("t4 %d %d %d\\n", iter -> to, tf3) : 0;
 44             cmax(tf3, f3[iter -> to] - 1);// && debug ? printf("t5 %d %d\\n", iter -> to, tf3) : 0;
 45             
 46             cmax(tf4, f1[x] + f3[iter -> to] - 1);
 47             cmax(tf4, f3[x] + f1[iter -> to] - 1);
 48             cmax(tf4, f2[x] + g1[iter -> to] - 1);
 49             cmax(tf4, f2[x] + f2[iter -> to] - 1);
 50             
 51             cmax(mx, f2[iter -> to]);
 52             cmax(mx, g1[iter -> to]);
 53             f1[x] = tf1; f2[x] = tf2; f3[x] = tf3; f4[x] = tf4; g1[x] = tg1; g2[x] = tg2;
 54 //            printf("f %d %d %d %d iter -> to %d %d\\n", tf1, tf2, tf3, tf4, iter -> to, x);
 55         }
 56     ++g1[x]; ++g2[x];
 57     f1[x] += deg; f2[x] += deg; f3[x] += deg; f4[x] += deg;
 58     /*cmax(g1[x], 1);*/ cmax(g2[x], g1[x]);
 59     cmax(f2[x], f1[x]); cmax(f3[x], f2[x]); cmax(f4[x], f3[x]);
 60     cmax(ans, g2[x]); cmax(ans, f4[x]);
 61 //    printf("ans %d x %d\\n", ans, x);
 62 //    x == 2 ? printf("f %d %d %d %d\\ng %d %d\\n", f1[x], f2[x], f3[x], f4[x], g1[x], g2[x]) : 0;
 63 }
 64 int main()
 65 {
 66     R int T, type; scanf("%d%d", &T, &type);
 67     for (; T; --T)
 68     {
 69         R int n; scanf("%d", &n); ans = 0;
 70         for (R int i = 1; i <= type; ++i) scanf("%*d%*d");
 71         for (R int i = 1; i < n; ++i)
 72         {
 73             R int a, b; scanf("%d%d", &a, &b); link(a, b);
 74         }
 75         if (n == 1) {puts("0"); continue;}
 76         dfs(1, 0);
 77         printf("%d\\n", ans);
 78         
 79         memset(last, 0, (n + 1) << 2); ecnt = e;
 80         memset(f1, 0, (n + 1) << 2);
 81         memset(f2, 0, (n + 1) << 2);
 82         memset(f3, 0, (n + 1) << 2);
 83         memset(f4, 0, (n + 1) << 2);
 84         memset(g1, 0, (n + 1) << 2);
 85         memset(g2, 0, (n + 1) << 2);
 86     }
 87     return 0;
 88 }
 89 /*
 90 1 0
 91 15
 92 1 2
 93 2 3
 94 1 4
 95 2 5
 96 1 6
 97 3 7
 98 4 8
 99 3 9
100 4 10
101 6 11
102 5 12
103 4 13
104 6 14
105 5 15
106 9
107 
108 1 0
109 10
110 1 2
111 2 3
112 1 4
113 3 5
114 3 6
115 2 7
116 1 8
117 7 9
118 9 10
119 6
120 
121 1 0
122 10
123 1 2
124 1 3
125 2 4
126 1 5
127 3 6
128 3 7
129 5 8
130 5 9
131 4 10
132 */
D2T1

 

 [Shoi2017]分手是祝愿

高斯消元有95分。

先把倍数关系通过一次高斯消元转化为单个灯是否要改变。

计c[i]表示还剩下i个1时的期望步数。根据题意,对于i<=k,有c[i] = i。然后这个转移有环,所以得高斯消元。

推出来每一行只有3个变量,所以消元是线性的。因为n和模数很接近,而消元的时候又有+1,然后系数很容易就+到0了,然后就boom(爆炸熊.jpg)。

既然出题人卡了高斯消元那么我们就来考虑推推式子。(因为没有用markdown所以式子略丑请原谅。。。)

考虑我们之前推出来的递推式:c[i] = (i/n) * c[i-1] + ((n - i) / n) * c[i + 1] + 1

然后对于i=n的时候,有c[n] = c[n - 1] + 1 (1)

对于i=n-1时,有c[n-1] = 1 + ((n - 1) / n) * c[n - 2] + (1 / n) * c[n] (2)

把(1)式代入(2)式:c[n - 1] = 1 + ((n - 1) / n) * c[n - 2] + 1 / n + 1 / n * c[n - 1]

移项:((n - 1) / n) * c[n - 1] = ((n - 1) / n) * c[n - 2] + (n + 1) / n

除过去:c[n - 1] = c[n - 2] + (n + 1) / (n - 1)

只剩两项啦~于是我们猜想c[i]这个序列是一个一阶递推式,并且上一项的系数是1(这个可以用数学归纳法证)。

于是我们就只要知道递推的常数项即可。我们来强行分析一波:

设第i项的系数为x[i],而且我们已知了x[n] = 1。递推式可以写成c[i] = c[i - 1] + x[i]

我们从一开始的式子入手:c[i] = (i / n) * c[i - 1] + ((n - i) / n) * c[i + 1] + 1

代入:c[i] = (i / n) * c[i - 1] + ((n - i) / n) * (c[i] + x[i + 1]) + 1

两边乘个n然后再移项:i * c[i] = i * c[i - 1] + (n - i) * x[i + 1] + n

除个i:c[i] = c[i - 1] + ((n - i) * x[i + 1] + n) / i

这样的话连上面系数等于1的顺便也给证了。。。

所以求得x[i] = ((n - i) * x[i + 1] + n) / i。

然后递推一下就能求出c每一项的值来啦。

附代码:(95分的高斯消元在注释里面)