题面:
得分就是单次提交所能获得的最高分。
1 2 3 4 5 6 7 8 9 10 11 void solve () { int n; cin>>n; int maxx=INT_MIN; for (int i=1 ;i<=n;++i){ int tmp; cin>>tmp; maxx=max (tmp,maxx); } cout<<maxx<<endl; }
模拟即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 void solve () { string x, s = "xwr" ; cin >> x; for (auto i : x) { int n = i - '0' ; string t; t.resize (3 ); for (int i = 0 ; i < 3 ; ++i) t[i] = (n & (1 << i) ? s[i] : '-' ); reverse (all (t)); cout << t; } cout << endl; }
注意到每个中心点的代表的面积一样的,那么并且如果一个点的上下左右四个方位中也有其他的点就一定是重叠了1单位的面积,容斥一下就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void solve () { int n; cin >> n; map<pair<int , int >, bool >f; int x, y; for (int i = 1 ; i <= n; ++i) { cin >> x >> y; f[{x, y}] = true ; } int num = 0 ; double ans = f.size () * 2 ; for (int i = 1 ; i <= 99 ; ++i) { for (int j = 1 ; j <= 99 ; ++j) { if (f[{i, j}]) { if (f[{i - 1 , j}])num++; if (f[{i, j - 1 }])num++; if (f[{i + 1 , j}])num++; if (f[{i, j + 1 }])num++; } } } num /= 2 ; cout << ans - num * 0.5 << endl; }
一个十进制数每一位都去掉了一个数字,问你在这个条件下这个十进制数是排在第几个。 现在想想每一位从原来的10种数字变成了9种数字,那不就是个十进制转换成九进制……怎么当时往数位dp上去想了
1 2 3 4 5 6 7 8 9 10 11 12 13 void solve () { int n,x; cin>>n>>x; int base=1 ,ans=0 ; while (n){ int tmp=n%10 ; n/=10 ; tmp -= tmp > x; ans += tmp*base; base *= 9 ; } cout<<ans+1 <<endl; }
写一下证明。 先令k == 2
,此时的LNC数是pow(2,n)-1
找哪些数是必胜哪些数是必败的: 1:最简单的情况,显然先手必胜。 2:因为此时比他小的LNC数只有1,拿掉1后剩1,对于下家来说只存在必胜态1,下家必赢,先手必败,当前状态是必败的。 3:此时比他小的LNC数也是只有1,拿掉1后剩2,下家是必败态,此时先手必胜。 推测奇数是必胜否则必败(1)。 证明:因为在二进制下的LNC数都是形如1111...111
这样的也就是说末尾一定是1,而必胜态是奇数,末尾是1,必败态末尾是0,那么一定存在一种方法使得先手在轮到自己的时候减去LNC数后是转向必败态,所以一定是必胜的。 然后令k == 3
,也就是k == 2
不行的时候,观察什么情况下k == 3
是必胜的。 此时的LNC数是1 2 4 8 1:先手必胜。 2:先手必胜。 3(10):先手可以拿掉1或者2,但都会转向必胜态,先手必败。 4(11):先手可以拿1或拿2,或者直接拿完,存在转向必败的情况,由于考虑最优策略先手为了赢肯定让必败态转到下家,所以先手必胜。 5(12):拿1转向必胜,拿2转向必败,拿4转向必胜,也存在转向必败的情况,先手必胜 6(20):拿掉1,2,4,对应变成5,4,2,都是必胜的,下家必胜,先手必败。 7(21):拿掉1,2,4,对应变成6,5,3,存在下家必败,先手必胜。 可以发现是1,2,3,4,5,6,7,8,9对应的输赢是赢赢输赢赢输赢赢输,推测n % 3 != 0
是必胜否则必败(2)。 结合(1)(2),推测n % k != 0
时一定是先手赢。 证明: 显然对于k进制下个位数一定是LNC(除了0以外,即[1,k-1]),而且如果该进制下个位数是0的话一定不是LNC,也就不能一次性全部拿走,那么n % k != 0
时,对于先手而言,如果当前不是个位数,那么就一定能拿的保证剩余的数在该进制下末位是0,下家就无法把它全部拿走,直到剩个位数的时候全部拿走就行。 有点像巴什博奕,改成一次只能拿[1,k-1]个石子也是一样的输赢结果。 实现的时候只要枚举进制就行,最小非因子考虑极端情况n是一些素数相乘这样才能尽可能大,这样n在1e18的范围是很小的。
1 2 3 4 5 6 void solve () { int x; cin>>x; for (int k=2 ;;++k) if (x%k!=0 ) return cout<<k<<endl,void (); }
队友找规律手算硬生生算出来打表的我也不知道怎么证明这个结果一定就是最小的,放下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 void solve () { int n,m; cin>>n>>m; if (n==3 and m==3 ){ cout<<R"(111 1*1 111)" ; }else if (n==3 and m==4 ){ cout<<R"(1111 1*11 1111)" ; }else if (n==3 and m==5 ){ cout<<R"(11111 1*1*1 11111)" ; }else if (n==3 and m==6 ){ cout<<R"(111111 1*1*11 111111)" ; }else if (n==3 and m==7 ){ cout<<R"(1111111 1*1*1*1 1111111)" ; }else if (n==3 and m==8 ){ cout<<R"(11111111 1*1*1*11 11111111)" ; }else if (n==3 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 111111111)" ; }else if (n==4 and m==3 ){ cout<<R"(111 1*1 111 111)" ; }else if (n==4 and m==4 ){ cout<<R"(1111 1*11 11*1 1111)" ; }else if (n==4 and m==5 ){ cout<<R"(11111 1*1*1 11+11 11111)" ; }else if (n==4 and m==6 ){ cout<<R"(111111 1*1*11 11*1*1 111111)" ; }else if (n==4 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1*11 1111111)" ; }else if (n==4 and m==8 ){ cout<<R"(11111111 1*1*1*11 11*1*1*1 11111111)" ; }else if (n==4 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1*1*11 111111111)" ; }else if (n==5 and m==3 ){ cout<<R"(111 1*1 111 1*1 111)" ; }else if (n==5 and m==4 ){ cout<<R"(1111 1*11 11+1 1*11 1111)" ; }else if (n==5 and m==5 ){ cout<<R"(11111 1*1*1 11+11 1*1*1 11111)" ; }else if (n==5 and m==6 ){ cout<<R"(111111 1*1*11 11+1+1 1*1*11 111111)" ; }else if (n==5 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1+11 1*1*1*1 1111111)" ; }else if (n==5 and m==8 ){ cout<<R"(11111111 1*1*1*11 11+1+1+1 1*1*1*11 11111111)" ; }else if (n==5 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1+1+11 1*1*1*1*1 111111111)" ; }else if (n==6 and m==3 ){ cout<<R"(111 1*1 111 1*1 111 111)" ; }else if (n==6 and m==4 ){ cout<<R"(1111 1*11 11*1 1*11 11*1 1111)" ; }else if (n==6 and m==5 ){ cout<<R"(11111 1*1*1 11+11 1*1*1 11+11 11111)" ; }else if (n==6 and m==6 ){ cout<<R"(111111 1*1*11 11*1*1 1*1*11 11*1*1 111111)" ; }else if (n==6 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1*11 1*1*1*1 11*1+11 1111111)" ; }else if (n==6 and m==8 ){ cout<<R"(11111111 1*1*1*11 11*1*1*1 1*1*1*11 11*1*1*1 11111111)" ; }else if (n==6 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1*1*11 1*1*1*1*1 11*1*1+11 111111111)" ; }else if (n==7 and m==3 ){ cout<<R"(111 1*1 111 1*1 111 1*1 111)" ; }else if (n==7 and m==4 ){ cout<<R"(1111 1*11 11+1 1*11 11*1 1*11 1111)" ; }else if (n==7 and m==5 ){ cout<<R"(11111 1*1*1 11+11 1*1*1 11+11 1*1*1 11111)" ; }else if (n==7 and m==6 ){ cout<<R"(111111 1*1*11 11+1*1 1*1*11 11*1+1 1*1*11 111111)" ; }else if (n==7 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1*11 1*1*1*1 11*1+11 1*1*1*1 1111111)" ; }else if (n==7 and m==8 ){ cout<<R"(11111111 1*1*1*11 11+1*1*1 1*1*1*11 11*1+1+1 1*1*1*11 11111111)" ; }else if (n==7 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1*1*11 1*1*1*1*1 11*1+1+11 1*1*1*1*1 111111111)" ; }else if (n==8 and m==3 ){ cout<<R"(111 1*1 111 1*1 111 1*1 111 111)" ; }else if (n==8 and m==4 ){ cout<<R"(1111 1*11 11*1 1*11 11*1 1*11 11*1 1111)" ; }else if (n==8 and m==5 ){ cout<<R"(11111 1*1*1 11+11 1*1*1 11+11 1*1*1 11+11 11111)" ; }else if (n==8 and m==6 ){ cout<<R"(111111 1*1*11 11*1*1 1*1*11 11*1*1 1*1*11 11*1*1 111111)" ; }else if (n==8 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1*11 1*1*1*1 11*1+11 1*1*1*1 11+1*11 1111111)" ; }else if (n==8 and m==8 ){ cout<<R"(11111111 1*1*1*11 11*1*1*1 1*1*1*11 11*1*1*1 1*1*1*11 11*1*1*1 11111111)" ; }else if (n==8 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1*1*11 1*1*1*1*1 11*1+1*11 1*1*1*1*1 11*1*1+11 111111111)" ; }else if (n==9 and m==3 ){ cout<<R"(111 1*1 111 1*1 111 1*1 111 1*1 111)" ; }else if (n==9 and m==4 ){ cout<<R"(1111 1*11 11+1 1*11 11*1 1*11 11*1 1*11 1111)" ; }else if (n==9 and m==5 ){ cout<<R"(11111 1*1*1 11+11 1*1*1 11+11 1*1*1 11+11 1*1*1 11111)" ; }else if (n==9 and m==6 ){ cout<<R"(111111 1*1*11 11+1*1 1*1*11 11*1*1 1*1*11 11*1+1 1*1*11 111111)" ; }else if (n==9 and m==7 ){ cout<<R"(1111111 1*1*1*1 11+1*11 1*1*1*1 11*1+11 1*1*1*1 11+1*11 1*1*1*1 1111111)" ; }else if (n==9 and m==8 ){ cout<<R"(11111111 1*1*1*11 11+1*1*1 1*1*1*11 11*1*1+1 1*1*1*11 11*1+1*1 1*1*1*11 11111111)" ; }else if (n==9 and m==9 ){ cout<<R"(111111111 1*1*1*1*1 11+1*1*11 1*1*1*1*1 11*1+1*11 1*1*1*1*1 11*1*1+11 1*1*1*1*1 111111111)" ; } }
首先遇到存在表示关系的问题,都可以考虑抽象成点与点之间的关系,然后就可以将关系转为边建图。 对于第$i$个人希望坐在$a_i$,可以理解为$i$到$a_i$有一条有向边。找几个样例画一下图: 所以前n个点的出度一定是1。并且可以发现相互之间存在关系的才会互相影响,又因为每个人都只有一个喜欢的座位,所以对于一块区域,如果有环,那么一定是只有一个环,且这个环生在这个区域的末尾,如第二张图。 (1)如果不是一条直链或者一整个环的区域,并且如果该区域有环,最大贡献就是环的大小。 因为如果区域内的从一个点开始断开和其他点的边,也就是不满足这个点的条件,那么这个点前面的点都不能满足。所以如果环上的点不满足,也就是环上的边断开了,这个断开处的点的前面所有点都不满足,又因为是一个环,其实也就变成了所有的点都不满足。所以存在环的区域贡献其实就是环的大小。这一点其实就是有向有环图为什么没有拓扑序,因为没有了先后关系。 (2)如果不是一条直链或者一整个环的区域,并且如果该区域没有环,最大贡献就是最长路。有向无环图,可以拓扑dp跑最长路。 (3)如果是一整个环的区域,归到(1)中解决。 (4)如果是一条直链的区域,归到(2)中解决。 具体的实现先处理自环,直接累加答案。然后用tarjan找环,对于强联通分量(SCC)的大小是大于1的SCC,累加SCC的大小。 处理完环之后跑拓扑序上的最长路dp,其实不用管环,因为环是在最后出现的并且每个点出度都是1,比如第二张图中到3就停下来了原因是2还有1连着它,做拓扑序的时候不满足前面的点都出队的条件,进不了队列,在3出队之后就停下来了有环并不影响结果。 然后统计所有有向无环的区域的汇点的最长路径累加即可。对于小于等于n的点,汇点可以用出度为0判断,对于大于n的点,汇点可以用只有一条入边,也就是入度为1来判断,因为大于n汇点是0有可能是和谁都没有连边的情况。并且要注意大于n的最长路要减去1,因为末尾大于n的那个位置并不是一个嘉宾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 struct SCC { int n; vector<vector<int >> edge, scc; vector<int > stk; vector<int > dfn, low, bel; int idx, cnt; SCC () {} SCC (int n) { init (n); } void init (int n) { this ->n = n; edge.assign (n, {}); dfn.assign (n, -1 ); low.resize (n); bel.assign (n, -1 ); stk.clear (); idx = cnt = 0 ; } void addEdge (int u, int v) { edge[u].push_back (v); } void dfs (int u) { dfn[u] = low[u] = idx++; stk.push_back (u); for (auto v : edge[u]) { if (dfn[v] == -1 ) dfs (v); if (bel[v] == -1 ) low[u] = min (low[u], low[v]); } if (dfn[u] == low[u]) { int v; vector<int > c; do { v = stk.back (); c.push_back (v); bel[v] = cnt; stk.pop_back (); } while (v != u); cnt++; scc.push_back (c); } } vector<int > work () { for (int i = 0 ; i < n; i++) if (dfn[i] == -1 ) dfs (i); return bel; } }; void solve () { int n; cin >> n; vector<int > deg (2 * n + 1 ) , out (2 * n + 1 ) ; vector<vector<int >> edge (n + 1 ); SCC scc (2 * n) ; int ans = 0 ; for (int i = 1 ; i <= n; ++i) { int t; cin >> t; scc.addEdge (i - 1 , t - 1 ); edge[i].push_back (t); if (i == t) ans++; deg[t]++; out[i]++; } vector<int > in (2 * n + 1 ) ; for (int i = 1 ; i <= 2 * n; ++i) in[i] = deg[i]; scc.work (); for (int i = 0 ; i < scc.cnt; ++i) if (scc.scc[i].size () > 1 ) ans += scc.scc[i].size (); queue<int > q; vector<int > dist (2 * n + 1 ) ; for (int i = 1 ; i <= n; ++i) if (deg[i] == 0 ) q.push (i), dist[i]++; while (!q.empty ()) { int u = q.front (); q.pop (); if (u > n) continue ; int v = edge[u][0 ]; dist[v] = max (dist[v], dist[u] + 1 ); if (--deg[v] == 0 ) q.push (v); } for (int i = 1 ; i <= 2 * n; ++i) { if (i <= n) { if (out[i] == 0 ) ans += dist[i]; } else { if (in[i]) ans += dist[i] - 1 ; } } cout << ans << endl; }
2024.08.06upd:好吧这个东西叫做基环树……当时没学vp的时候硬是用tarjan做出来了
剩下的金银牌题以后再补。