Catcats Contest 系列
2021/4/19 18:58:22
本文主要是介绍Catcats Contest 系列,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
NOI 赛前抱 3 个月的佛脚(ku
完成度:5/17
Catcats Contest #1
字符串 (count)
神必转化,壬都傻了
假设一个串有 \(i\) 个 \(1\) 和 \(n-i\) 个 \(0\),则定义 \(0\) 的权值是 \(i\),\(1\) 的权值是 \(i-n\),一个字符串的权值是其所有字符权值之和。
所有长为 \(l\) 的循环子串的权值的平均值为 \(0\),且两个相同长度的字符串相差不超过 \(1\) 当且仅当权值相差不超过 \(n\),且相同长度的字符串权值\(\bmod n\) 的值都相同。
所以,一个字符串是好的当且仅当所有子串的权值 \(\in(-n,n)\),也就是说 \(\exist k\in[0,n)\) 使得所有前缀和 \(\in(k-n,k]\)。
从大到小枚举 \(k\),此时字符串是唯一的,维护出这个字符串然后顺便计算出不符合询问串限制的位置数量,如果为 \(0\) 说明满足条件。
时间复杂度 \(O(n^2)\),而且还可以得到答案也是 \(\le n^2\) 的。注意不要算重了。
#include<bits/stdc++.h> #define PB emplace_back using namespace std; const int N = 1030; int T, n, ans, pre[N], cnt; bool num[N]; char str[N]; vector<int> vec[N]; void solve(){ scanf("%s", str+1); n = strlen(str+1); ans = 0; for(int i = 0;i <= n;++ i){ cnt = pre[0] = 0; vec[0].PB(0); for(int j = 1;j <= n;++ j){ if(pre[j-1] + i < n){ pre[j] = pre[j-1] + i; num[j] = false; cnt += str[j] == '1'; } else { pre[j] = pre[j-1] + i - n; num[j] = true; cnt += str[j] == '0'; } if(pre[j] >= 0) vec[pre[j]].PB(j); } for(int j = n-1;;-- j){ ans += !vec[j].empty() && !cnt; if(!j) break; for(int k : vec[j]){ cnt += (str[k] == '0') + (str[k+1] == '1') - (str[k] == '1') - (str[k+1] == '0'); swap(num[k], num[k+1]); } } for(int j = 0;j < n;++ j) vec[j].resize(0); } printf("%d\n", ans); } int main(){scanf("%d", &T); while(T --) solve();}
Catcats Contest #2
己酸集合 (geo)
每个询问分别做,对左右边界跑 two-pointer。时间复杂度 \(O(nq)\)。
构造题 (buhui)
IOI2006 的加强版,有点东西(/kel
首先发现一个性质:如果凸包上 \(S,T\) 不是连续段,则一定无解。构造证明其他情况都有解。
主要思路是把凸包范围做一个三角剖分,然后每个三角形内部分别做。
不知为何考虑递归构造,设 \(\texttt{Solve(A,B,C)}\) 表示 \(A,B,C\) 是逆时针方向,其中 \(A\) 与 \(B,C\) 分别属于不同集合,\(BC\) 有连边,你需要将 \(\Delta ABC\) 内部的所有与 \(A\) 所属集合相同的点与 \(A\) 连通,与 \(B\) 所属集合相同的点与 \(B\) 或 \(C\) 中一个连通。
若内部点全都与 \(B,C\) 所属集合相同,就可以随便分治了,因为全部相同所以只要保证是一棵生成树即可。
否则任意选择一个与 \(A\) 所属集合相同的点 \(X\),与 \(A\) 连边,将该三角形划分为三个小三角形,变成了子问题 \(\texttt{Solve(X,B,C),Solve(C,A,X),Solve(B,X,A)}\),递归做。
至于三角剖分,分类讨论一下:
- 若凸包上的点所属集合全相同,那么就在内部找一个不同的,以其为中心做三角剖分。
- 若凸包上的点所属集合不同,则一定是一段 \(S\) 还有一段 \(T\),两边可以分别做,具体实现见代码(雾
在随机数据下的时间复杂度大概是 \(O(n\log n)\),最坏是 \(O(n^2)\)。
#include<bits/stdc++.h> #define PB emplace_back #define fi first #define se second using namespace std; typedef long long LL; typedef vector<int> VI; typedef pair<int, int> pii; const int N = 3003; template<typename T> void read(T &x){ int ch = getchar(); x = 0; for(;ch < '0' || ch > '9';ch = getchar()); for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; } int n, m; struct Point { int x, y; Point(int _x = 0, int _y = 0): x(_x), y(_y){} bool operator < (const Point &o) const {if(x != o.x) return x < o.x; return y < o.y;} LL operator * (const Point &o) const {return (LL)x * o.y - (LL)y * o.x;} Point operator - (const Point &o) const {return Point(x - o.x, y - o.y);} Point operator + (const Point &o) const {return Point(x + o.x, y + o.y);} } p[N]; vector<pii> ans[2]; inline bool col(int x){return x > n;} void adde(int x, int y){ assert(col(x) == col(y)); if(col(x)) ans[1].PB(x - n, y - n); else ans[0].PB(x, y); } bool intri(int a, int b, int c, int d){ // d in angle bac ? Point v1 = p[b] - p[a], v2 = p[c] - p[a], v3 = p[d] - p[a]; if(v1 * v2 < 0) swap(v1, v2); return v1 * v3 > 0 && v3 * v2 > 0; } void work(int a, int b, int c, VI now){ // printf("a = %d, b = %d, c = %d\n", a, b, c); if(now.empty()) return; VI tmp; for(int i : now) if(col(i) == col(a)){ adde(a, i); tmp.resize(0); for(int j : now) if(intri(i, a, b, j)) tmp.PB(j); work(b, i, a, tmp); tmp.resize(0); for(int j : now) if(intri(i, b, c, j)) tmp.PB(j); work(i, b, c, tmp); tmp.resize(0); for(int j : now) if(intri(i, c, a, j)) tmp.PB(j); work(c, a, i, tmp); return; } static LL dis[N]; for(int i : now) dis[i] = labs((p[c] - p[b]) * (p[i] - p[b])); sort(now.begin(), now.end(), [&](int u, int v){return dis[u] < dis[v];}); adde(b, now[0]); for(int i = 1;i < now.size();++ i) adde(now[i-1], now[i]); } int main(){ read(n); read(m); for(int i = 1;i <= n+m;++ i){read(p[i].x); read(p[i].y);} VI id(n+m), hul; iota(id.begin(), id.end(), 1); sort(id.begin(), id.end(), [&](int a, int b){return p[a] < p[b];}); for(int i : id){ while(hul.size() > 1 && (p[i] - p[hul.back()]) * (p[i] - p[hul[hul.size()-2]]) > 0) hul.pop_back(); hul.PB(i); } id.pop_back(); reverse(id.begin(), id.end()); int tmp = hul.size(); for(int i : id){ while(hul.size() > tmp && (p[i] - p[hul.back()]) * (p[i] - p[hul[hul.size()-2]]) > 0) hul.pop_back(); hul.PB(i); } hul.pop_back(); int pos = 1; for(;pos < hul.size() && col(hul[0]) == col(hul[pos]);++ pos); // printf("pos = %d\n", pos); if(pos == hul.size()){ for(int i = 1;i < hul.size();++ i) adde(hul[i-1], hul[i]); hul.PB(hul[0]); for(int i = 1;i <= n+m;++ i) if(col(i) != col(hul[0])){ for(int j = 1;j < hul.size();++ j){ int a = hul[j-1], b = hul[j]; VI tmp; for(int k = 1;k <= n+m;++ k) if(intri(i, a, b, k)) tmp.PB(k); work(i, a, b, tmp); } break; } } else { rotate(hul.begin(), hul.begin() + pos, hul.end()); // for(int i : hul) printf("%d ", i); putchar('\n'); for(pos = 1;pos < hul.size() && col(hul[0]) == col(hul[pos]);++ pos); for(int _ = pos;_ < hul.size();++ _) if(col(hul[0]) == col(hul[_])){puts("Impossible"); return 0;} for(int i = 1;i < pos;++ i){ int a = hul[pos], b = hul[i-1], c = hul[i]; VI tmp; for(int k = 1;k <= n+m;++ k) if(intri(a, b, c, k)) tmp.PB(k); adde(b, c); work(a, b, c, tmp); } for(int i = pos+1;i < hul.size();++ i){ int a = hul[0], b = hul[i-1], c = hul[i]; VI tmp; for(int k = 1;k <= n+m;++ k) if(intri(a, b, c, k)) tmp.PB(k); adde(b, c); work(a, b, c, tmp); } } assert(ans[0].size() == n-1); assert(ans[1].size() == m-1); for(int _ = 0;_ < 2;++ _) for(pii &u : ans[_]) printf("%d %d\n", u.fi, u.se); }
最长环 (route)
人类智慧,肝败吓疯(
#include<bits/stdc++.h> using namespace std; const int N = 303, B = 666, NIF = -1000000007; template<typename T> void read(T &x){ int ch = getchar(); x = 0; for(;ch < '0' || ch > '9';ch = getchar()); for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0'; } template<typename T> bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;} mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); int n, m, k, x[N], y[N], z[N], a[N][N], d[2][N][N], w[N][N][3], ans; bool f[N], o; void ins(int u, int v, int x){ int p = 0; for(;p < 3;++ p){ int t = w[u][v][p]; if(a[u][t] + a[v][t] < a[u][x] + a[v][x]) break; } if(p < 3){ for(int i = 2;i > p;-- i) w[u][v][i] = w[u][v][i-1]; w[u][v][p] = x; } } void calc(int u, int v, int x, int y){ int p = 0; while(w[u][v][p] == x || w[u][v][p] == y) ++ p; int z = w[u][v][p]; chmax(d[o][x][y], a[x][u] + a[u][z] + a[z][v] + a[v][y]); } void solve(int k){ for(int i = 0;i <= n;++ i) for(int j = 0;j <= n;++ j) d[o][i][j] = NIF; switch(k){ case 1: for(int i = 1;i <= n;++ i) if(f[i] == o) d[o][i][i] = 0; break; case 2: for(int i = 1;i <= m;++ i) if(f[x[i]] == o && f[y[i]] == o) d[o][x[i]][y[i]] = d[o][y[i]][x[i]] = z[i]; break; case 3: for(int i = 1;i <= m;++ i) if(f[x[i]] == o && f[y[i]] == o) for(int j = 1;j <= m;++ j) if(i != j && f[x[j]] == o && f[y[j]] == o){ if(x[i] == x[j]) chmax(d[o][y[i]][y[j]], z[i] + z[j]); else if(y[i] == x[j]) chmax(d[o][x[i]][y[j]], z[i] + z[j]); else if(x[i] == y[j]) chmax(d[o][y[i]][x[j]], z[i] + z[j]); else if(y[i] == y[j]) chmax(d[o][x[i]][x[j]], z[i] + z[j]); } break; case 4: for(int i = 1;i <= m;++ i) if(f[x[i]] == o && f[y[i]] == o) for(int j = 1;j <= m;++ j) if(i != j && f[x[j]] == o && f[y[j]] == o && x[i] != x[j] && x[i] != y[j] && y[i] != x[j] && y[i] != y[j]){ chmax(d[o][x[i]][x[j]], z[i] + z[j] + a[y[i]][y[j]]); chmax(d[o][x[i]][y[j]], z[i] + z[j] + a[y[i]][x[j]]); chmax(d[o][y[i]][x[j]], z[i] + z[j] + a[x[i]][y[j]]); chmax(d[o][y[i]][y[j]], z[i] + z[j] + a[x[i]][x[j]]); } break; case 5: for(int i = 1;i <= n;++ i) if(f[i] == o) for(int j = 1;j <= n;++ j) if(f[j] == o){ w[i][j][0] = w[i][j][1] = w[i][j][2] = 0; } for(int i = 1;i <= m;++ i) if(f[x[i]] == o && f[y[i]] == o) for(int j = 1;j <= m;++ j) if(i != j && f[x[j]] == o && f[y[j]] == o){ if(x[i] == x[j]) ins(y[i], y[j], x[i]); else if(y[i] == x[j]) ins(x[i], y[j], y[i]); else if(x[i] == y[j]) ins(y[i], x[j], x[i]); else if(y[i] == y[j]) ins(x[i], x[j], y[i]); } for(int i = 1;i <= m;++ i) if(f[x[i]] == o && f[y[i]] == o) for(int j = 1;j <= m;++ j) if(i != j && f[x[j]] == o && f[y[j]] == o && x[i] != x[j] && x[i] != y[j] && y[i] != x[j] && y[i] != y[j]){ calc(x[i], x[j], y[i], y[j]); calc(y[i], x[j], x[i], y[j]); calc(x[i], y[j], y[i], x[j]); calc(y[i], y[j], x[i], x[j]); } } } int main(){ read(n); read(m); read(k); for(int i = 0;i <= n;++ i) for(int j = 0;j <= n;++ j) a[i][j] = NIF; for(int i = 1;i <= m;++ i){ read(x[i]); read(y[i]); read(z[i]); a[x[i]][y[i]] = a[y[i]][x[i]] = z[i]; } for(int _ = 0;_ < B;++ _){ for(int i = 1;i <= n;++ i) f[i] = rng() & 1; if(k == 6){o = 0; solve(2); o = 1; solve(4);} else {o = 0; solve(k>>1); o = 1; solve(k+1>>1);} for(int i = 1;i <= m;++ i) if(f[x[i]] && !f[y[i]]) swap(x[i], y[i]); for(int i = 1;i <= m;++ i) if(f[x[i]] != f[y[i]]) for(int j = 1;j <= m;++ j) if(i != j && f[x[j]] != f[y[j]]) chmax(ans, d[0][x[i]][x[j]] + d[1][y[i]][y[j]] + z[i] + z[j]); } if(ans > 0) printf("%d\n", ans); else puts("impossible"); }
Catcats Contest #3
计数题 (count)
壬被玩傻了
首先 \(n,m\) 都为奇数时答案是 \(1\)。
其次 \(n\) 为偶数,\(m\) 为奇数时答案是 \((n/2+1)^{(m-1)/2}\),这事因为 \(1\) 所在列已经被确定,每个要填的列都有 \(n/2+1\) 种独立的方式。
然后就是 \(n,m\) 都为偶数的情况。若将整个矩阵划分为 \(2\times 2\) 的 block,则每块都恰有一个 \(1\),且每行、每列填的 \(1\) 的位置形如 \(010101|101010\),我们按列做 dp,将这个分界线的位置记录下来,并且将第 \(i\) 个 block 内的 \(1\) 是否靠右记录下来。要做的就是避免下面这种情况:
.x.. ..x. ...x x... x... ...x ..x. .x..
转移是一个 SOS 形式,时间复杂度 \(O(n^3m2^n)\),其中这里的 \(n,m\) 是原来的一半。
#include<bits/stdc++.h> using namespace std; typedef long long LL; int n, m, lim, mod, dp[2][13][4096], a[4096], ans; bool cr; int ksm(int a, int b){ int res = 1; for(;b;b >>= 1, a = (LL)a * a % mod) if(b & 1) res = (LL)res * a % mod; return res; } void qmo(int &x){x += x >> 31 & mod;} void FMT(int *a){ for(int mid = 1;mid < lim;mid <<= 1) for(int i = 0;i < lim;i += mid<<1) for(int j = i;j < i+mid;++ j) qmo(a[j] += a[j|mid] - mod); } int main(){ scanf("%d%d%d", &n, &m, &mod); if((n & 1) && (m & 1)){puts("1"); return 0;} if(n & 1) swap(n, m); if(m & 1){printf("%d\n", ksm((n>>1)+1, m>>1)); return 0;} n>>=1; m>>=1; lim = 1<<n; for(int i = 0;i <= n;++ i) for(int j = 0;j < lim;++ j) dp[0][i][j] = 1; for(int _ = 1;_ < m;++ _, cr ^= 1){ memset(dp[!cr], 0, sizeof dp[!cr]); for(int i = 0;i <= n;++ i){ memcpy(a, dp[cr][i], lim<<2); FMT(a); for(int j = 0;j <= n;++ j) for(int S = 0;S < lim;++ S){ int T = S; for(int k = i+1;k < j;++ k) if((S>>k-1&1) && !(S>>k&1)) T |= 1<<k; for(int k = j;k < i-1;++ k) if(!(S>>k&1) && (S>>k+1&1)) T |= 1<<k; qmo(dp[!cr][j][S] += a[T] - mod); } } } for(int i = 0;i <= n;++ i) for(int j = 0;j < lim;++ j) qmo(ans += dp[cr][i][j] - mod); printf("%d\n", ans); }
Catcats Contest #4
平方过百万 (million)
有一个我不会的转化:对于第 \(2\) 种操作,在 \((u_p,v_p)\) 之间加一条权值为操作编号的边,则编号为 \(T\) 的询问 \(\texttt{1 u}\) 即为求从 \(u\) 开始走权值递增且 \(\le T\) 的边,能到达的节点个数。
可以使用点分治,假设当前处理的分治中心是 \(r\),对每个操作 \(2\),求出从它到达分治中心的最早时间,和分治中心能到达它所出发的最晚时间。这大概是一个二维偏序,用排序 + 树状数组即可,时间复杂度 \(O((n+m)\log n)\)。有点烦,自闭了
Catcats Contest #5
构造题 (buhui)
stm 域论,等拿到数学女孩第 5 册了就学(
有限域相关基础:\(\text{GF}(p^n)\) 可以表示为系数模 \(p\),整体模 \(n\) 次本原多项式 \(F\) 的意义下定义四则运算,由不超过 \(n-1\) 次的多项式构成的代数结构。
可以证明这东西是域,也可以证明它存在且唯一。其特征为 \(p\),所以 \((x+y)^p=x^p+y^p\)。
那至于这题怎么做:取最小的 \(w\) 使得 \(2^w>|S|\),计算出 \(\text{GF}(2^w)\),构造 \(a_i=i\cdot 2^w+f(i)\),其中 \(f(i)\) 是 \(\text{GF}(2^w)\) 意义下的立方。
至于正确性,考虑反证法:假设存在 \((i_1,j_1)\ne (i_2,j_2)\) 使得 \(a_{i_1}\oplus a_{j_1}=a_{i_2}\oplus a_{j_2}\),则 \(i_1,j_1,i_2,j_2\) 必须两两不同,且在 \(\text{GF}(2^w)\) 意义下 \(i_1+j_1=i_2+j_2\ne 0\),且 \(i_1^3+j_1^3=i_2^3+j_2^3\),得到 \(i_1^2+i_1j_1+j_1^2=i_2^2+i_2j_2+j_2^2\),所以 \(i_1j_1=i_2j_2\)。
令 \(s=i_1+j_1,p=i_1j_1\),得到 \(x(s-x)=p\) 有至少 \(4\) 个根,而其只有 \(2\) 次,矛盾。
因此 \(a_i\oplus a_j\) 两两不同,得证。本原多项式可以事先预处理,具体实现上 mod 2 运算可以使用异或运算。
这个构造的背景在于一个经典问题:如何在 \([0,p)^2\) 中构造 \(p\) 个整点使得不存在三点共线,其中 \(p\) 是质数。一种构造是取 \(x_i=i,y_i=i^2\bmod p\),可以得到 \(\frac{y_j-y_i}{x_j-x_i}=\frac{y_k-y_j}{x_k-x_j}\Rightarrow x_i+x_j=x_j+x_k\Rightarrow x_i=x_k\)。
#include<bits/stdc++.h> using namespace std; const int _[] = {2,7,11,19,37,67,131,283,515,1033,2053,4105}; int n, mod, lim; int red(int x){ int p = mod; while(p <= x) p <<= 1; for(;p >= mod;p >>= 1) if((x ^ p) < x) x ^= p; return x; } int mul(int x, int y){ int r = 0; for(;x;x >>= 1, y <<= 1) if(x & 1) r ^= y; return red(r); } int main(){ scanf("%d", &n); mod = _[n-1]; lim = 1<<n; for(int i = 0;i < lim;++ i) printf("%d ", i<<n|mul(mul(i,i),i)); }
这篇关于Catcats Contest 系列的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享