CF576
CF576
CF576A
CF576A题意
给定一个数字 \(n\),现在 Vasya 要从 \(1\sim n\) 中想一个数字 \(x\)。
Petya 向 Vasya 询问 “\(x\) 是否能整除 \(y\)?” ,通过 Vasya 的回答来判断 \(x\) 的答案。
Petya 的问题一开始就已经准备好,他必须将所有问题都问一遍,不管他当前需不需要问。
他想知道无论 Vasya 想出任何的 \(x\),
最少要准备多少次询问 \(y\) 的问题才能猜中 \(x\),并输出一组具体方案。
\(n\leq 1000\)
简要题意:需要你构造一个序列 \(a\),使得任意的 \(x\in[1,n]\),由 \(a|x\) 的真假构成的 \(01\) 序列 \(b\) 独一无二。
CF576A题解
对于每一个数字 \(x\),我们可以将其质因数分解变成 \(p_1^{x_1}p_2^{x_2}\dots p_k^{x_k}\)。
不难发现,对于数字 \(x\),如果序列中有 \(p_1^{x_1}\) 和 \(p_1^{x_1+1}\),那么就一定能够判定这个数字有 \(p_1^{x_1}\) 这个因数。
故构造一个序列满足 \(\{x|x=prime_i^k(prime_i^k \le n),prime_i为质数\}\) 就能满足条件。
没了。
CF576A代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1200; int prime[maxn], tot; bool book[maxn]; int n; vector<int> vec; signed main(){ n = read(); for(int i = 2;i <= n;i++){ if(!book[i])prime[++tot] = i; for(int j = 1;j <= tot && prime[j] * i <= n;j++){ book[prime[j] * i] = 1; if(i % prime[j] == 0)break; } } for(int i = 1;i <= tot && prime[i] <= n;i++){ int tmp = prime[i]; while(tmp <= n){vec.push_back(tmp);tmp *= prime[i];} } printf("%d\n",vec.size()); for(int i : vec)printf("%d ",i); puts(""); return 0; }
CF576B
link
用时:1:14:51.25
CF576B题意
给定一个 \([1,n]\) 的排列 \(p\),如果设一条边表示为 \((u,v)\),则要求你构造一棵树 \(T\) 满足
给出构造方案或者报告无解。
CF576B题解
啥笔Eric被B题卡,好似
如果将 \(i\to p_i\),不难发现这样的图有以下性质:
- 所有的点入度和出度都是 \(1\)。
- 第一条可以推出这个图一定是由若干个环构成的,证明显然。
- 如果在 \(T\) 中 \(\exists(u,v)\),则 \(u,v\) 的后继节点也必须相连。
所以不难发现,以下两种情况都是一定有解的:
- 有一个环是自环,则所有点连向这个点一定满足所有条件。
- 有一个环只有两个点,剩下的所有环都有偶数个点。
其余情况一律无解。
构造?
- 有一个环是自环的情况,设这个点是 \(x\),那么这个点的后继点也是 \(x\),也就是说,当 \((x,i)\) 相连的时候,\((x,p_i)\) 也必须相连。而刚刚的构造是将所有 \((x,i),i\neq x\) 相连,而所有的 \(i\neq j\) 都不存在 \((i,j)\),故一定满足条件。
- 有一个环只有两个点,设其为 \((x,p_x)\),其余所有环都有偶数个点,那么从环上一个位置开始顺时针编号,让所有奇数编号连向 \(x\),所有偶数编号连向 \(p_x\),最后在连上 \((x,p_x)\),那么一定可以满足所有条件。
证明显然,可以自己手玩一下,异或严谨证明都可。
CF576B代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e5 + 10; int n, p[maxn]; bool book[maxn]; vector<pair<int,int> > vec; signed main(){ n = read(); for(int i = 1;i <= n;i++)p[i] = read(); int u = 0;for(int i = 1;i <= n;i++)if(p[i] == i){u = i;break;} if(u){ puts("YES"); for(int i = 1;i <= n;i++) if(i != u)printf("%d %d\n",i,u); return 0; } u = 0; for(int i = 1;i <= n;i++)if(p[p[i]] == i){u = i;book[p[u]] = book[u] = 1;break;} if(!u){puts("NO");return 0;} else{ for(int i = 1;i <= n;i++){ if(!book[i]){ int cnt = 0, v = p[i];book[i] = 1; while(v != i){book[v] = 1;cnt++;v = p[v];} if((cnt & 1) == 0){puts("NO");return 0;} v = p[i];vec.push_back(make_pair(p[u],i)); while(v != i){vec.push_back(make_pair(u, v));v = p[v];u = p[u];} } } vec.push_back(make_pair(u,p[u])); puts("YES"); for(auto i : vec)printf("%d %d\n",i.first,i.second); } return 0; }
CF576C
link
用时1:29:36.12
CF576C题意
在一个平面上有 \(n(n\le 10^6)\) 个点,每个点 \(i\) 有坐标 \((x_i,y_i)(0\le x_i,y_i\le10^6)\)。
现在要求你给出一个排列,使得按照排列顺序行进的曼哈顿距离总和小于 \(2.5\times10^9\)。
要求给出方案,保证有解。
CF576C题解
从未设想的道路
将坐标 \((x_i,y_i)\) 看做区间 \([x_i,y_i]\),那么 \(i\to j\) 的曼哈顿距离就是在莫队上从 \([x_i,y_i]\) 转移到 \([x_j,y_j]\) 的最小步数。
莫队的奇偶排序就是干这个的,直接 \(sort\) 就完了。
CF576C代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e6 + 10; int len, n; struct node{ int l, r, id; node(int l = 0,int r = 0,int id = 0):l(l),r(r),id(id){} friend bool operator< (node a,node b){ if(a.l / len != b.l / len)return a.l < b.l; return ((a.l / len) & 1) ? a.r < b.r : a.r > b.r; } }a[maxn]; signed main(){ len = sqrt(n = read()); for(int i = 1;i <= n;i++){ int x = read(), y =read(); a[i] = node(x,y,i); } sort(a + 1,a + 1 + n); for(int i = 1;i <= n;i++)printf("%d ",a[i].id); puts(""); return 0; }
CF576D
link
用时:2:39:40.05
CF576D题意
给出一张 \(n\) 点 \(m\) 边的有向图。
每条边 \(i\) 可以经过多次,但是在此之前,必须经过 \(d_i\) 次边(重复经过算多次)才能经过这条边。
问最少经过多少条边才能从 \(1\to n\),无解输出 Impossible
。
\(1\le n,m\le 150,0\le d_i\le10^9\)
CF576D题解
需要对 Floyd
有较深的理解。
设矩阵 \(A^k\) 表示经过 \(k\) 条边 可以/不可以 从 \(i\to j\)。
设矩阵 \(B_k\) 表示由所有 \(d_i<k\) 的边组成的邻接矩阵。
不难发现,\(A^k=(B_k)^k\),这东西可以矩阵快速幂搞。
然后思路就有了:先将边从小到大排序,然后枚举每条边,设这条边是 \(i\),那么依次进行以下操作:
- 首先更新 \(A^{tim_{i-1}}\) 到 \(A^{time_i}\)。
- 然后将 \(B_{u_i,v_i}\) 标记为 \(1\)。
- 将所有 \(B_{1,i}=1\) 的 \(i\) 加入队列,跑 \(bfs\)。
- 更新答案 \(ans\gets dis_n+time_i\)
最后注意初始状态下 \(A\) 是单位矩阵,\(B\) 是全 \(0\) 矩阵。
CF576D代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 155; int n, m; struct edge{ int u, v, w; edge(int u = 0,int v = 0,int w = 0):u(u),v(v),w(w){} friend bool operator < (edge a,edge b){return a.w < b.w;} }edg[maxn]; struct Matrix{ bitset<maxn> a[maxn]; Matrix(bool opt = 0){for(int i = 1;i <= n;i++){a[i].reset(n);a[i][i] = opt;}} friend Matrix operator * (Matrix a,Matrix b){ Matrix c = Matrix(); for(int i = 1;i <= n;i++) for(int k = 1;k <= n;k++) if(a.a[i][k]) c.a[i] |= b.a[k]; return c; } friend Matrix operator ^ (Matrix a,int b){ Matrix res = Matrix(1); while(b){ if(b & 1)res = res * a; a = a * a;b >>= 1; } return res; } }A,B; int dis[maxn]; queue<int> que; void bfs(){ memset(dis,0x3f,sizeof(dis)); while(!que.empty())que.pop(); for(int i = 1;i <= n;i++) if(A.a[1][i]){que.push(i);dis[i] = 0;} if(!dis[n])return; while(!que.empty()){ int u = que.front();que.pop(); for(int v = 1;v <= n;v++) if(B.a[u][v] && dis[v] == 0x3f3f3f3f3f3f3f3f){ dis[v] = dis[u] + 1;que.push(v); } } } signed main(){ n = read(); m = read();int u, v, w; for(int i = 1;i <= m;i++){ u = read(); v = read(); w = read(); edg[i] = edge(u, v, w); } sort(edg + 1,edg + 1 + m); int tim = 0,ans = 0x3f3f3f3f;dis[n] = 0x3f3f3f3f; A = Matrix(1);B = Matrix();edg[m + 1].w = -1; for(int i = 1;i <= m;i++){ if(ans < edg[i].w)break; int dif = edg[i].w - tim;tim = edg[i].w; A = A * (B ^ dif); B.a[edg[i].u][edg[i].v] = 1; if(edg[i].w != edg[i + 1].w)bfs(); ans = min(ans,tim + dis[n]); } if(ans == 0x3f3f3f3f)puts("Impossible"); else printf("%lld\n",ans); return 0; }
CF576E
CF576E题意
给定一张 \(n\) 个点 \(m\) 条边的无向图。
一共有 \(k\) 种颜色,一开始,每条边都没有颜色。
定义合法状态为仅保留染成 \(k\) 种颜色中的任何一种颜色的边,图都是一张二分图。
有 \(q\) 次操作,第 \(i\) 次操作将第 \(e_i\) 条边的颜色染成 \(c_i\)。
但并不是每次操作都会被执行,只有当执行后仍然合法,才会执行本次操作。
你需要判断每次操作是否会被执行。
\(n,m,q \le 5 \times 10^5\),\(k \le 50\)。
CF576E题解
线段树分治现学的。
不会线段树分治的自行右转到线段树分治模板。
现在默认大家都会线段树分治。
考虑这道题怎么用。
首先开 \(k\) 个线段树维护这是肯定的了。
如果所有的执行都会成立,怎么做?
对于同一条边 \(i\),对于两个相邻的染色操作 \(q_x=(i,a)\) 和 \(q_y=(i,b)\),\(q_x\) 的生效区间是 \([x+1,y-1]\),这个完全可以线段树分治搞。
但是现在有的操作不会执行,怎么办呢?
首先不难发现操作执行就是把 \([x+1,y-1]\) 赋值为新颜色,否则赋值为原来的颜色。
回忆一下线段树分治是怎么做的。
一定会先更新完 \(i\) 再去更新 \(x+1\) 对吧?
于是我们就会在下一个边加入之前确定这个边的颜色。
然后就没了。
不过需要注意下空间。
CF576E代码
#include<bits/stdc++.h> using namespace std; inline int read(){ int x = 0, f = 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } bool st; const int maxn = 5e5 + 10; int n, m, k, q; pair<int,int> edg[maxn]; int id[maxn], col[maxn]; int r[maxn]; struct DSU{ typedef pair<pair<int,int>,int> T; struct Stack{ T s[maxn];int tp; inline T top(){return s[tp];} inline bool pop(){if(!tp)return false;tp--;return true;} inline void push(T x){s[++tp] = x;} Stack(){tp = 0;} }; stack<T> opt; int fa[maxn << 1], hei[maxn << 1]; int getf(int x){return fa[x] == x ? x : getf(fa[x]);} void init(int x = 0){for(int i = 1;i <= x;i++)fa[i] = i, hei[i] = 1;} void merge(int x,int y){ x = getf(x); y = getf(y); if(x == y)return; if(hei[x] > hei[y])swap(x, y); opt.push(make_pair(make_pair(x, y),hei[x] == hei[y])); fa[x] = y;hei[y] += hei[x] == hei[y]; } void trace(){ if(!opt.size())return; auto u = opt.top();opt.pop(); fa[u.first.first] = u.first.first; hei[u.first.second] -= u.second; } void trace(int lsttop){ while(opt.size() != lsttop){ auto u = opt.top();opt.pop(); fa[u.first.first] = u.first.first; hei[u.first.second] -= u.second; } } inline int version(){return opt.size();} }; DSU dsu[51]; int lst[maxn], ncol[maxn]; bool ans[maxn]; struct Seg_Tree{ typedef int T; vector<T> d[maxn << 2]; void init(int x = 0,int y = 0){for(int i = 1;i <= x;i++)dsu[i].init(y * 2);} void update(int l,int r,int s,int t,int p,T x){ if(s <= l && r <= t){d[p].push_back(x);return;} int mid = l + r >> 1; if(s <= mid)update(l,mid,s,t,p << 1,x); if(mid < t)update(mid + 1,r,s,t,p << 1 | 1,x); } void getans(int l,int r,int p){ vector<int> lsttop;lsttop.clear();lsttop.push_back(0); for(int i = 1;i <= k;i++){lsttop.push_back(dsu[i].version());} for(T i : d[p]){ int pos = id[i], col = ::col[i]; int u = edg[pos].first, v = edg[pos].second; if(col){ dsu[col].merge(u, v + n); dsu[col].merge(u + n,v); } } if(l == r){ int pos = id[l], col = ::col[l]; int u = edg[pos].first, v = edg[pos].second; int fu = dsu[col].getf(u), fv = dsu[col].getf(v); if(fu == fv)puts("NO"),::col[l] = ncol[pos]; else puts("YES"),ncol[pos] = ::col[l]; } if(l != r){ int mid = l + r >> 1; getans(l,mid,p << 1); getans(mid + 1,r,p << 1 | 1); } for(int i = 1;i <= k;i++){dsu[i].trace(lsttop[i]);} } void update(int l,int r,T x){update(1,q,l,r,1,x);} }tree; bool ed; signed main(){ // cerr << (&ed - &st) / 1024 / 1024 << " Mib" << endl; n = read(); m = read(); k = read(); q = read(); tree.init(k,n);int u, v; for(int i = 1;i <= m;i++){ u = read(); v = read(); edg[i] = make_pair(u, v); } for(int i = 1;i <= q;i++){id[i] = read(); col[i] = read();ans[i] = true;} for(int i = 1;i <= m;i++){lst[i] = q + 1;ncol[i] = 0;} for(int i = q;i;i--){ r[i] = lst[id[i]];lst[id[i]] = i; if(i < r[i] - 1)tree.update(i + 1,r[i] - 1,i); } tree.getans(1,q,1); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】