我等蒟蒻爆零之后,问LincHpin大爷:“此等神题可有甚么来头?”
LincHpin:“此三题皆为当年ZXR前辈所留。”
固名之,ZXR专场,233~~~
T1 勤奋的YouSiki
这个题在BZOJ和HDU上都有身影,一样不一样的吧,反正意思差不多,想法也很相近。
首先就是发现mex函数的一个性质——当左端点固定时,函数值随右端点单调,即$mex(i,j) leq mex(i,j+1)$。
然后,我们这么做:先$O(N)$求出以1位左端点,右端点分别为$i=1..n$的mex函数值,然后不断将左端点向右移动。
当左端点从$i$移动到$i+1$时,会使得$num_{i}$从维护序列中消失,考虑对那些mex函数值的影响。
首先,$mex_{1}$到$mex_{i}$的函数值已经没用了,可以忽略。
如果我们已经知道$num_{i}$下一次出现的位置是$next_{i}$,那么从$i$到$next_{i}-1$区间内的所有mex值都应当对$num_{i}$取min。
又因为mex函数值一直保持单调性,所有可以二分出来需要修改的区间,然后就变成了区间修改,区间求和,那就是线段树了。

1 #include <cstdio> 2 #include <cstring> 3 4 inline char nextChar(void) 5 { 6 static const int siz = 1 << 20; 7 8 static char buf[siz]; 9 static char *hd = buf + siz; 10 static char *tl = buf + siz; 11 12 if (hd == tl) 13 fread(hd = buf, 1, siz, stdin); 14 15 return *hd++; 16 } 17 18 inline int nextInt(void) 19 { 20 register int ret = 0; 21 register bool neg = false; 22 register char bit = nextChar(); 23 24 for (; bit < 48; bit = nextChar()) 25 if (bit == '-')neg ^= true; 26 27 for (; bit > 47; bit = nextChar()) 28 ret = ret * 10 + bit - '0'; 29 30 return neg ? -ret : ret; 31 } 32 33 typedef long long lnt; 34 35 const int siz = 500005; 36 37 int n, num[siz]; 38 39 int nxt[siz]; 40 int lst[siz]; 41 int mex[siz]; 42 43 bool vis[siz]; 44 45 inline void prework(void) 46 { 47 for (int i = 0; i <= n; ++i) 48 lst[i] = n + 1; 49 50 for (int i = n; i >= 1; --i) 51 { 52 nxt[i] = lst[num[i]]; 53 lst[num[i]] = i; 54 } 55 56 memset(vis, false, sizeof(vis)); 57 58 int ans = 0; 59 60 for (int i = 1; i <= n; ++i) 61 { 62 vis[num[i]] = true; 63 64 while (vis[ans]) 65 ++ans; 66 67 mex[i] = ans; 68 } 69 } 70 71 lnt sum[siz << 2]; 72 lnt tag[siz << 2]; 73 int son[siz << 2]; 74 75 void build(int t, int l, int r) 76 { 77 tag[t] = -1; 78 79 if (l == r) 80 sum[t] = son[t] = mex[l]; 81 else 82 { 83 int mid = (l + r) >> 1; 84 85 build(t << 1, l, mid); 86 build(t << 1 | 1, mid + 1, r); 87 88 son[t] = son[t << 1 | 1]; 89 sum[t] = sum[t << 1] + sum[t << 1 | 1]; 90 } 91 } 92 93 inline void addtag(int t, lnt v, lnt k) 94 { 95 son[t] = v; 96 tag[t] = v; 97 sum[t] = v * k; 98 } 99 100 inline void pushdown(int t, int l, int r) 101 { 102 int mid = (l + r) >> 1; 103 addtag(t << 1, tag[t], mid - l + 1); 104 addtag(t << 1 | 1, tag[t], r - mid); 105 tag[t] = -1; 106 } 107 108 lnt query(int t, int l, int r, int x, int y) 109 { 110 if (l == x && r == y) 111 return sum[t]; 112 else 113 { 114 if (~tag[t]) 115 pushdown(t, l, r); 116 117 int mid = (l + r) >> 1; 118 119 if (y <= mid) 120 return query(t << 1, l, mid, x, y); 121 else if (x > mid) 122 return query(t << 1 | 1, mid + 1, r, x, y); 123 else return 124 query(t << 1, l, mid, x, mid) 125 + query(t << 1 | 1, mid + 1, r, mid + 1, y); 126 } 127 } 128 129 int query(int t, int l, int r, int v) 130 { 131 if (son[t] <= v) 132 return n + 1; 133 134 if (l == r) 135 return l; 136 137 if (~tag[t]) 138 pushdown(t, l, r); 139 140 int mid = (l + r) >> 1; 141 142 int s = son[t << 1]; 143 144 if (s > v) 145 return query(t << 1, l, mid, v); 146 else 147 return query(t << 1 | 1, mid + 1, r, v); 148 } 149 150 void change(int t, int l, int r, int x, int y, lnt v) 151 { 152 if (l == x && r == y) 153 addtag(t, v, r - l + 1); 154 else 155 { 156 if (~tag[t]) 157 pushdown(t, l, r); 158 159 int mid = (l + r) >> 1; 160 161 if (y <= mid) 162 change(t << 1, l, mid, x, y, v); 163 else if (x > mid) 164 change(t << 1 | 1, mid + 1, r, x, y, v); 165 else 166 { 167 change(t << 1, l, mid, x, mid, v); 168 change(t << 1 | 1, mid + 1, r, mid + 1, y, v); 169 } 170 171 son[t] = son[t << 1 | 1]; 172 sum[t] = sum[t << 1] + sum[t << 1 | 1]; 173 } 174 } 175 176 inline void solve(void) 177 { 178 lnt ans = 0LL; 179 180 prework(); 181 182 build(1, 1, n); 183 184 for (int i = 1; i <= n; ++i) 185 { 186 ans += query(1, 1, n, i, n); 187 188 int pos = query(1, 1, n, num[i]); // first > num[i] 189 190 if (pos <= nxt[i] - 1) 191 change(1, 1, n, pos, nxt[i] - 1, num[i]); 192 } 193 194 printf("%lld ", ans); 195 } 196 197 signed main(void) 198 { 199 freopen("mex.in", "r", stdin); 200 freopen("mex.out", "w", stdout); 201 202 n = nextInt(); 203 204 for (int i = 1; i <= n; ++i) 205 num[i] = nextInt(); 206 207 for (int i = 1; i <= n; ++i) 208 if (num[i] > n) 209 num[i] = n; 210 211 solve(); 212 213 fclose(stdin); 214 fclose(stdout); 215 216 return 0; 217 }
网上还有一个比较神的算法,复杂度据说是有保证的,小伙伴说是$O(NlogN)$的,我等蒟蒻目瞪口呆。
令$sum_{i}=sum_{1 leq j leq i}{mex_{j,i}}$,发现这东西也具有单调性,及$sum_{i} leq sum_{i+1}$。
然后另i从1不断变大,即sum的右端点不断右移。每次考虑新加入$num_{i}$对当前$sum$的影响。只需要计算出有多少个sum值需要+1即可,还需要暴力枚举数字,不知道为什么复杂度是有保证的,跑得还迷之快~~~
1 #include <cstdio> 2 3 const int siz = 500005; 4 5 int n, num[siz], lst[siz], pos[siz]; 6 7 signed main(void) 8 { 9 freopen("mex.in", "r", stdin); 10 freopen("mex.out", "w", stdout); 11 12 scanf("%d", &n); 13 14 for (int i = 1; i <= n; ++i) 15 scanf("%d", num + i); 16 17 long long ans = 0LL, now = 0LL; 18 19 for (int i = 1, p; i <= n; ++i, ans += now) 20 if (num[i] < n) 21 { 22 p = lst[num[i]]; 23 lst[num[i]] = i; 24 25 for (int j = num[i]; j < n; ++j) 26 { 27 pos[j] = lst[j]; 28 if (j && pos[j] > pos[j - 1] ) 29 pos[j] = pos[j - 1]; 30 if (pos[j] > p) 31 now += pos[j] - p; 32 else break; 33 } 34 } 35 36 printf("%lld ", ans); 37 38 return fclose(stdin), fclose(stdout), 0; 39 }
T2 贪玩的YouSiki
贪心思想。首先左边一列点是行,右边一列点是列,中间连线表示点。然后不断找交错路,在上面交错染色。注意染色的顺序即可。别问为啥,证明请找大爷。
1 #include <cstdio> 2 #include <algorithm> 3 4 const int siz = 600005; 5 6 int n, X[siz], Y[siz]; 7 8 int mapX[siz], totX; 9 int mapY[siz], totY; 10 11 int hd[siz], to[siz], nt[siz], tot = 1, ans[siz]; 12 13 inline void add(int a, int b) 14 { 15 nt[++tot] = hd[a]; to[tot] = b; hd[a] = tot; 16 nt[++tot] = hd[b]; to[tot] = a; hd[b] = tot; 17 } 18 19 void dfs(int u, int k) 20 { 21 int e = hd[u]; 22 while (e && !to[e]) 23 e = nt[e]; 24 hd[u] = nt[e]; 25 if (e) 26 { 27 to[e ^ 1] = 0; 28 ans[e >> 1] = k; 29 dfs(to[e], !k); 30 } 31 } 32 33 signed main(void) 34 { 35 freopen("game.in", "r", stdin); 36 freopen("game.out", "w", stdout); 37 38 scanf("%d", &n); 39 40 for (int i = 1; i <= n; ++i) 41 { 42 scanf("%d%d", X + i, Y + i); 43 mapX[++totX] = X[i]; 44 mapY[++totY] = Y[i]; 45 } 46 47 std::sort(mapX + 1, mapX + 1 + totX); 48 std::sort(mapY + 1, mapY + 1 + totY); 49 50 totX = std::unique(mapX + 1, mapX + 1 + totX) - mapX; 51 totY = std::unique(mapY + 1, mapY + 1 + totY) - mapY; 52 53 for (int i = 1; i <= n; ++i) 54 { 55 X[i] = std::lower_bound(mapX + 1, mapX + totX, X[i]) - mapX; 56 Y[i] = std::lower_bound(mapY + 1, mapY + totY, Y[i]) - mapY; 57 } 58 59 for (int i = 1; i <= n; ++i) 60 add(X[i], Y[i] + totX - 1); 61 62 for (int i = 1; i <= totX + totY - 2; ++i) 63 for (int j = 0; hd[i]; j = !j)dfs(i, j); 64 65 for (int i = 1; i <= n; ++i) 66 printf("%d", ans[i]); 67 68 return fclose(stdin), fclose(stdout), 0; 69 }
T3 另一个YouSiki
首先发现,这和数字大小没什么关系,只需要看是不是0就行,毕竟一个正数再怎么乘,再怎么加也还是正数。
然后把01矩阵看成邻接矩阵,$A^{K}$就是走$K$步,从$i$到$j$的路径方案数。题目又保证有至少一个自环,所以只需要关心连通性问题,即这个图是否满足从任意一个点都能到达其他所有点。这个,tarjan吧。
1 #include <cstdio> 2 #include <cstring> 3 4 inline char nextChar(void) 5 { 6 static const int siz = 1 << 20; 7 8 static char buf[siz]; 9 static char *hd = buf + siz; 10 static char *tl = buf + siz; 11 12 if (hd == tl) 13 fread(hd = buf, 1, siz, stdin); 14 15 return *hd++; 16 } 17 18 inline int nextInt(void) 19 { 20 register int ret = 0; 21 register bool neg = false; 22 register char bit = nextChar(); 23 24 for (; bit < 48; bit = nextChar()) 25 if (bit == '-')neg ^= true; 26 27 for (; bit > 47; bit = nextChar()) 28 ret = ret * 10 + bit - '0'; 29 30 return neg ? -ret : ret; 31 } 32 33 const int siz = 1000005; 34 35 int n, hd[siz], to[siz], nt[siz], tot; 36 37 inline void add(int u, int v) 38 { 39 nt[++tot] = hd[u]; to[tot] = v; hd[u] = tot; 40 } 41 42 int dfn[siz], low[siz], tim, flag; 43 44 inline void Min(int &a, int b) 45 { 46 if (a > b)a = b; 47 } 48 49 void tarjan(int u) 50 { 51 dfn[u] = low[u] = ++tim; 52 53 for (int i = hd[u]; i; i = nt[i]) 54 if (!dfn[to[i]]) 55 tarjan(to[i]), Min(low[u], low[to[i]]); 56 else 57 Min(low[u], dfn[to[i]]); 58 59 if (low[u] == dfn[u]) 60 flag &= (u == 1); 61 } 62 63 signed main(void) 64 { 65 freopen("another.in", "r", stdin); 66 freopen("another.out", "w", stdout); 67 68 for (int cas = nextInt(); cas--; ) 69 { 70 n = nextInt(); 71 72 memset(hd, 0, sizeof(hd)), tot = 0; 73 memset(dfn, 0, sizeof(dfn)), tim = 0; 74 75 for (int i = 1; i <= n; ++i) 76 for (int j = 1; j <= n; ++j) 77 if (nextInt())add(i, j); 78 79 flag = true; 80 81 tarjan(1); 82 83 for (int i = 1; i <= n; ++i) 84 flag &= (dfn[i] != 0); 85 86 puts(flag ? "YES" : "NO"); 87 } 88 89 return fclose(stdin), fclose(stdout), 0; 90 }
@Author: YouSiki