第八届中国大学生程序设计竞赛总决赛(CCPC Final 2022)
Preface
真是坐坐又牢牢啊,2h40min 过了 A 后就开始发呆,剩下的题一个也做不来
但好在会的几个题全部一遍过,最后罚时尚可还能混进 Au 区
A. Graph Partitioning
考虑一条连接 \(x,y(x 因此构建一个二分图,左边是 \(2n-2\) 条边对应的点,右边是两棵树中 \(2n-2\) 个需要确定父亲节点的点,左边的每个点都会连接右边的两个点 不难发现有解的充要条件是这张图存在完美匹配,而稍做分析会发现由于这个二分图的点数等于边数,因此有完美匹配的充要条件为每个连通块都是基环树 而基环树的完美匹配方案一定为 \(2\)(环上某条边的匹配确定后,剩下的都唯一确定,一共有两个方向),因此最后方案数就是 \(2\) 的连通块幂次 #include #include #define RI register int #define CI const int& using namespace std; const int N=2000005,mod=998244353; int n,x,y,fa[N],num[N],sz[N]; inline int getfa(CI x) { return fa[x]!=x?fa[x]=getfa(fa[x]):x; } inline void Link(CI x,CI y) { fa[getfa(x)]=getfa(y); } int main() { scanf("%d",&n); if (n==1) return puts("1"),0; for (RI i=1;i<=4*n-4;++i) fa[i]=i; for (RI i=1;i<=2*n-2;++i) { scanf("%d%d",&x,&y); if (x==y) return puts("0"),0; if (x>y) swap(x,y); Link(i,2*n-2+y-1); Link(i,3*n-3+x); } for (RI i=1;i<=4*n-4;++i) ++sz[getfa(i)]; for (RI i=1;i<=2*n-2;++i) num[getfa(i)]+=2; // for (RI i=1;i<=4*n-4;++i) printf("%d%c",sz[i]," \n"[i==4*n-4]); // for (RI i=1;i<=4*n-4;++i) printf("%d%c",num[i]," \n"[i==4*n-4]); int cnt=0; for (RI i=1;i<=4*n-4;++i) { if (getfa(i)!=i) continue; if (num[i]!=sz[i]) return puts("0"),0; else ++cnt; } int ans=1; for (RI i=1;i<=cnt;++i) ans=2LL*ans%mod; return printf("%d",ans),0; } C. DFS Order 3 这题之前在 2023 合肥区域赛的热身赛上做过,这次就直接秒了 #include void work() { int n; std::cin >> n; std::vector for(auto &d: d) for(auto &d: d) std::cin >> d, d--; std::vector std::vector vis[0] = true; for(int i = 1; i < n; ++i) { int cur = d[0][i]; for(int j = 0; j < n; ++j) if(vis[d[cur][j]]) { ans.push_back({cur, d[cur][j]}); break; } vis[cur] = true; } for(auto [f, t]: ans) std::cout << f + 1 << " " << t + 1 << char(10); return ; } int main() { std::ios::sync_with_stdio(false); int T; std::cin >> T; while(T--) work(); return 0; } E. CCPC String 枚举中心的 \(P\) 所在的位置,显然两边最长能延申的距离是可以二分的 #include #include #include #define int long long #define RI register int #define CI const int& using namespace std; const int N=1e6+5; int t,pfx[N]; char s[N]; signed main() { for (scanf("%lld",&t);t;--t) { scanf("%s",s+1); int n=strlen(s+1),ans=0; for (RI i=1;i<=n;++i) pfx[i]=pfx[i-1]+(s[i]!='p'?1:0); for (RI i=1;i<=n;++i) { if (s[i]=='c') continue; int l=1,r=min((i-1)/2,n-i),ret=0; auto check=[&](CI l,CI r) { return pfx[r]-pfx[l-1]==r-l+1; }; while (l<=r) { int mid=l+r>>1; if (check(i-mid*2,i-1)&&check(i+1,i+mid)) ret=mid,l=mid+1; else r=mid-1; } ans+=ret; } printf("%lld\n",ans); } return 0; } F. Chase Game 3 如果存在某个 \(i,i+1\),使得它们在排列上的距离 \(\ge 3\),此时后手一定不能获胜 因为此时 \(i,i+1\) 两个点在排列上的邻居一定不同,先手只要在 \(i,i+1\) 之间反复走到与后手当前位置不相邻的点即可 #include using namespace std; const int N = 4e5+5; int pos[N]; bool solve() { int n; cin >> n; for (int i=1; i<=n; ++i) { int a; cin >> a; pos[a] = i; } for (int i=1; i if (abs(pos[i]-pos[i+1]) > 2) return false; } return true; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int T; cin >> T; while (T--) cout << (solve() ? "Yes\n" : "No\n"); return 0; } J. Best Carry Player 3 考虑找到 \(k\) 二进制下的最高位 \(t\),显然异或操作只会改变后 \(t\) 位 因此最优的策略大致为:先用异或操作将最后 \(t\) 为变为全 \(1\),再通过 \(+1\) 把前面的部分增加 但具体实现时需要注意各种 Corner Case,比如 \(k=2^t-1\) 时可以两步变换,否则至少要三步;还有一些边界情况的处理 #include #include #define int long long #define RI register int #define CI const int& using namespace std; int t,x,y,k; signed main() { for (scanf("%lld",&t);t;--t) { scanf("%lld%lld%lld",&x,&y,&k); if (x>y) swap(x,y); if (x==y) { puts("0"); continue; } if (k<=1) { printf("%lld\n",y-x); continue; } int hb=1; while (hb<=k) hb<<=1; int xf=x/hb,xb=x%hb,yf=y/hb,yb=y%hb,ans=0; auto calc=[&](CI x,CI y) { if (x==y) return 0; return ((x^y)<=k||y-x<=1)?1:2; }; if (xf==yf) { printf("%lld\n",calc(xb,yb)); continue; } if (xb==(hb-1)) ans+=1; else if (((hb-1)^xb)<=k) ans+=2; else ans+=3; ++xf; xb=0; if (xf!=yf) ans+=(yf-xf)*(k==hb-1?2:3),xf=yf; ans+=calc(xb,yb); printf("%lld\n",ans); } return 0; } L. Completely Multiplicative Function 徐神在我的疯狂质疑下还是秒了这题,令人感慨 首先假设所有位置初始时都是 \(1\),现在考虑要将若干位置变成 \(-1\) 不难发现 \([\frac{n}{2},n]\) 内的所有素数的值都可以任意改变,这就给了我们一个调整的余量 而对于 \([1,\frac{n}{2}]\) 内的素数显然先修改小的能改变的位置数更多,我们直接爆搜,在有余量调整的情况下跑的飞快 #include using llsi = long long signed int; constexpr bool check = false; bool is_not_prime[1000005]; std::vector void init(int n) { is_not_prime[0] = is_not_prime[1] = 1; for(int i = 2; i <= n; ++i) if(!is_not_prime[i]) { prime.push_back(i); for(llsi j = llsi(i) * i; j <= n; j += i) is_not_prime[j] = 1; } return ; } void work(int n, int k) { if(n % 2 != k % 2) return std::cout << "-1\n", void(0); k = (n - k) / 2; std::vector const std::vector int cur = 0; auto upd = [&](int pos, bool newVal) { cur -= _b[pos]; cur += _b[pos] = newVal; }; int hkr = 0; for(int i = (n + 2) / 2; i <= n; ++i) if(!is_not_prime[i]) hkr++; if(hkr < k) for(auto pr: prime) { if(pr >= (n + 2) / 2) return void(std::cerr << "FUCK\n"), void(std::cout << "-1\n"); for(llsi P = pr; P <= n; P *= pr) for(llsi i = 1; i <= n / P; ++i) upd(i * P, b[i * P] ^ 1); if(cur > k) { for(llsi P = pr; P <= n; P *= pr) for(llsi i = 1; i <= n / P; ++i) upd(i * P, b[i * P] ^ 1); } else if(cur + hkr >= k) { break; } } // std::cerr << "cur = " << cur << char(10); for(int i = (n + 2) / 2; cur < k && i <= n; ++i) if(!is_not_prime[i]) upd(i, b[i] ^ 1); if constexpr (check) { for(int i = 1; i <= n; ++i) for(int j = 1; i * j <= n; ++j) { if((b[i] ^ b[j]) != b[i * j]) { std::cerr << "n, k = " << n << ", " << k << " failed at " << i << " * " << j << char(10); goto __break__; } } __break__:; } for(int i = 1; i <= n; ++i) std::cout << (b[i] ? "-1" : "1") << char(i == n ? 10 : 32); return ; } int main() { std::ios::sync_with_stdio(false); init(1000000); // for(int i = 0; i <= 1000; ++i) for(int k = (i & 1); k <= i; k += 2) work(i, k); // return 0; int T; std::cin >> T; while(T--) { int n, k; std::cin >> n >> k; work(n, k); } return 0; } Postscript 唉沟槽的 Final,感觉下个月要在场上狠狠罚坐了
Copyright © 2022 世界杯历史|1998年世界杯冠军是谁|福乐产权的世界杯商业权益站|flchanquan.com All Rights Reserved.