CSP2020-S题解
A.儒略日
傻逼题,以后再说吧
B.动物园
傻逼题,以后也不会说
C.函数调用
考场冲的裸线段树可以获得 70pts 的好分数,数据 8 太行
solution:
容易发现这是一张 DAG
考虑只有全局乘的 case,发现可以通过一次记搜获得每个点的总共要乘的权值
然后把 f 数组调用的函数节点的权值乘起来 O(n) 更新原数组就行
那么有单点修改呢?可以发现如果一个点被加了 x,那他对答案的实际贡献是 $x \times mul$, mul 就是这个操作之后的所有乘法操作的积
一个位于 $f_i$ 子图里的一个加法操作的 mul 是多少呢?首先 $[f_j, f_n]$ 这个区间里的所有乘法操作的积是一定包含的,这已经在刚刚记搜中搞完了
现在就差 $f_i$ 子图里的乘法积了,怎么搞呢?容易发现,如果我们按照他 3 操作给的顺序从左到右建边的话,这个加法操作在这个子图中的积就是这个操作右侧的所有 mul 的乘积
所以我们可以通过 topo 排序获得答案,一个点向下枚举时顺序从右向左
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdlib> 6 #include <vector> 7 #include <queue> 8 #include <cctype> 9 #define INF 0x7fffffff 10 #define infP 100010 11 12 namespace lrg { 13 14 namespace io { 15 const int SIZE = (1 << 21) + 1; 16 char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr; 17 18 #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++) 19 20 inline void flush () { 21 fwrite (obuf, 1, oS - obuf, stdout); 22 oS = obuf; 23 } 24 25 inline void putc (char x) { 26 *oS ++ = x; 27 if (oS == oT) flush (); 28 } 29 30 template <class I> 31 inline void read (I &x) { 32 for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1; 33 for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); 34 x *= f; 35 } 36 37 template <class I> 38 inline void print (I x) { 39 if (!x) putc ('0'); 40 if (x < 0) putc ('-'), x = -x; 41 while (x) qu[++ qr] = x % 10 + '0', x /= 10; 42 while (qr) putc (qu[qr --]); 43 } 44 45 struct Flusher_ {~Flusher_(){flush();}}io_flusher_; 46 } 47 using io :: read; using io :: putc; using io :: print; 48 template <class T> 49 inline void read(T &a, T &b) {read(a); read(b);} 50 template <class T> 51 inline void read(T &a, T &b, T &c) {read(a); read(b); read(c);} 52 53 inline void setting() { 54 #ifdef ONLINE_JUDGE 55 freopen("call.in", "r", stdin); 56 freopen("call.out", "w", stdout); 57 #endif 58 } 59 60 const int ha = 998244353; 61 62 int n, m, q; 63 int a[infP], f[infP]; 64 int in[infP]; 65 int sum[infP], mul[infP]; 66 std::vector <int> opt[infP]; 67 std::queue <int> Q; 68 69 inline int type(int x) {return opt[x][0];} 70 71 int dfsMul(int x) { 72 static bool vis[infP]; 73 if(vis[x]) return mul[x]; vis[x] = 1; 74 if(type(x) == 1) return mul[x] = 1; 75 if(type(x) == 2) return mul[x] = opt[x][1]; 76 mul[x] = 1; 77 for(auto i = opt[x].begin() + 1; i != opt[x].end(); i++) 78 mul[x] = 1ll * mul[x] * dfsMul(*i) % ha; 79 return mul[x]; 80 } 81 82 inline void topoSum() { 83 for(int i = 1; i <= m; i++) { 84 if(in[i] == 0) Q.push(i); 85 } 86 while(!Q.empty()) { 87 int x = Q.front(); Q.pop(); 88 int now = 1; 89 if(type(x) != 3) continue; 90 for(auto i = opt[x].end() - 1; i != opt[x].begin(); i--) { 91 int y = *i; 92 sum[y] = (sum[y] + 1ll * now * sum[x] % ha) % ha; 93 now = 1ll * now * mul[y] % ha; 94 if(--in[y] == 0) Q.push(y); 95 } 96 } 97 } 98 99 inline void main () { 100 setting(); 101 read(n); 102 for(int i = 1; i <= n; i++) read(a[i]); 103 read(m); 104 for(int i = 1; i <= m; i++) { 105 int t; read(t); opt[i].push_back(t); 106 if(t == 1) { 107 int p, v; read(p, v); 108 opt[i].push_back(p); 109 opt[i].push_back(v); 110 } else if(t == 2) { 111 int v; read(v); 112 opt[i].push_back(v); 113 } else { 114 int c, f; read(c); 115 for(int j = 1; j <= c; j++) read(f), opt[i].push_back(f), ++in[f]; 116 } 117 } 118 read(q); 119 for(int i = 1; i <= q; i++) read(f[i]); 120 std::reverse(f + 1, f + 1 + q); 121 for(int i = 1; i <= m; i++) { 122 if(in[i] == 0) dfsMul(i); 123 } 124 for(int i = 1, now = 1; i <= q; i++) { 125 sum[f[i]] = (sum[f[i]] + now) % ha; 126 now = (1ll * now * mul[f[i]] % ha); 127 } 128 topoSum(); 129 int tot = 1; 130 for(int i = 1; i <= q; i++) tot = 1ll * tot * mul[f[i]] % ha; 131 for(int i = 1; i <= n; i++) a[i] = 1ll * a[i] * tot % ha; 132 for(int i = 1; i <= m; i++) { 133 if(type(i) != 1) continue; 134 a[opt[i][1]] = (a[opt[i][1]] + 1ll * opt[i][2] * sum[i] % ha) % ha; 135 } 136 for(int i = 1; i <= n; i++) print(a[i]), putc(' '); 137 putc('\n'); 138 return; 139 } 140 141 } 142 143 signed main () { 144 lrg::main () ; 145 return 0; 146 }
D.贪吃蛇
容易发现一个性质
一般情况下,n 号蛇吃掉 1 号蛇后下一秒,如果他不在最头上也不在最末尾,那么 n-1 号蛇吃掉 2 号蛇之后一定在他前面,因为 $a_{n}-a_1 > a_{n-1}-a_2$
有了这条性质再来看本题
如果一个蛇吃了蛇,那么他只有两种case:1.他不在最头上;2.他在最头上
首先思考 case1:如果他不在最头上,那么来考虑一下 n-1 号蛇,又因为上面的性质,所以如果这两条蛇后面会被吃,那一定是 n-1 号蛇先被吃,所以这整个问题都递归到了 n-1 号蛇 case
也就是说,如果 case1,那么这条蛇一定会选择吃,这很好,问题只剩 case2 了
子 case1:如果 n-1 号蛇选择吃了之后问题成为 case1,那么蛇 n-1 一定会吃掉蛇 n,那么 n 一定会选择不吃,讨论结束
子 case2:如果 n-1 号蛇吃了之后变成了 case2,那么递归至子 case1
容易发现,这个 game 一定会在 n 和 n-1 中的一方上停止,也容易发现,停止在谁身上和 case2 的递归次数的奇偶性有关
所以,做完了
???真的做完了嘛?
显然我们的蛇权值数组需要是有序的,那么一条蛇开餐后,这个新蛇应该塞到哪里呢?
我们需要维护一个数组,支持$O(1)$删除头节点,查询任意位置,任意位置插入
这好像做不了吧?
所以我们考虑一个这个题一开始的性质,就是后面吃的蛇的剩余权值一定比前面的小,所以可以发现,吃完后的权值也是具有单调性的
所以,我们不妨维护两个双端队列,第一个是初始蛇,第二个是塞的都是剩余权值
我们要维护他的单调递增性质,所以任意时刻,如果这个时刻的蛇要选择进餐,那一定是这两个队列的尾较大的吃掉两个队列的头的较小的
因为我们还要维护单调性,所以剩余权值直接塞进 deque2 的头,正确性见上面性质
所以,这才真正做完了
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cstdlib> 6 #include <vector> 7 #include <queue> 8 #include <cctype> 9 #include <deque> 10 #define ll long long 11 #define INF 0x7fffffff 12 #define inf 1000010 13 14 namespace lrg { 15 16 namespace io { 17 const int SIZE = (1 << 21) + 1; 18 char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr; 19 20 #define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++) 21 22 inline void flush () { 23 fwrite (obuf, 1, oS - obuf, stdout); 24 oS = obuf; 25 } 26 27 inline void putc (char x) { 28 *oS ++ = x; 29 if (oS == oT) flush (); 30 } 31 32 template <class I> 33 inline void read (I &x) { 34 for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1; 35 for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); 36 x *= f; 37 } 38 39 template <class I> 40 inline void print (I x) { 41 if (!x) putc ('0'); 42 if (x < 0) putc ('-'), x = -x; 43 while (x) qu[++ qr] = x % 10 + '0', x /= 10; 44 while (qr) putc (qu[qr --]); 45 } 46 47 struct Flusher_ {~Flusher_(){flush();}}io_flusher_; 48 } 49 using io :: read; using io :: putc; using io :: print; 50 template <class T> 51 inline void read(T &a, T &b) {read(a); read(b);} 52 template <class T> 53 inline void read(T &a, T &b, T &c) {read(a); read(b); read(c);} 54 55 inline void setting() { 56 freopen("snakes.in", "r", stdin); 57 freopen("snakes.out", "w", stdout); 58 } 59 60 #define pii std::pair <int, int> 61 62 int n, ans; 63 int a[inf]; 64 65 std::deque <pii> Q; 66 std::deque <pii> E; 67 68 inline void clear() { 69 Q.clear(); 70 E.clear(); 71 ans = 1; 72 } 73 74 inline void solve() { 75 static int test1 = 1; 76 if(test1) { 77 read(n); 78 for(int i = 1; i <= n; i++) read(a[i]); 79 test1 = 0; 80 } else { 81 clear(); 82 int k; read(k); 83 for(int i = 1; i <= k; i++) { 84 int x, y; read(x, y); 85 a[x] = y; 86 } 87 } 88 for(int i = 1; i <= n; i++) Q.push_back(std::make_pair(a[i], i)); 89 int now = 0; 90 while(Q.size() + E.size() > 2) { 91 int eaten = 0; 92 if(E.empty() || Q.front() < E.front()) eaten = Q.front().first, Q.pop_front(); 93 else eaten = E.front().first, E.pop_front(); 94 pii act; 95 if(E.empty() || Q.back() > E.back()) act = Q.back(), Q.pop_back(); 96 else act = E.back(), E.pop_back(); 97 pii minus = std::make_pair(act.first - eaten, act.second); 98 pii comp = std::min(Q.front(), E.empty() ? Q.front() : E.front()); 99 if(minus < comp) { 100 if(now == 0) ans = Q.size() + E.size() + 1; 101 ++now; 102 } else if(now) break; 103 E.push_front(minus); 104 } 105 ans += (now & 1); 106 printf("%d\n", ans); 107 return; 108 } 109 110 inline void main () { 111 setting(); 112 int T; read(T); 113 while(T--) solve(); 114 return; 115 } 116 117 } 118 119 signed main () { 120 lrg::main () ; 121 return 0; 122 }
这场比赛题都挺有意思的(除了A),给个好评(除了A),但是自己这场得分好像全靠 rp 啊/wul
具体游记见我 luogu blog
NOIP2020 ++rp, ++score;!!!

浙公网安备 33010602011771号