【题解】CF1824 合集
CF1824A LuoTianyi and the Show
标签:思维题
\(C\)
我们可以较为容易地得出一个贪心策略,就是先去放一个以第 \(3\) 中方式入座的人,再在两边放 \(1,2\) 种方式的人,如果放的时候占用了第三种方式的人的座位就跳过该座位,最后将剩下的以第 \(3\) 中方式入座的人放进去。
当然还有可能只放 \(1,3\) 或 \(2,3\) 种方式的人
至于现将哪个以第三种方式入座的人放进去,我们可以进行枚举,容易地,我们可以 \(O(1)\) 求得以每一个座位开始的答案。
code:
#include<bits/stdc++.h> using namespace std; const int NN = 1e5 + 8; int t,n,m; int a[NN]; int vis[NN]; int main(){ scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); int cnt1 = 0,cnt2 = 0,cnt3 = 0; int ans1 = 0,ans2 = 0,ans3 = 0; memset(a,0,sizeof(a)); memset(vis,0,sizeof(vis)); for(int i = 1; i <= n; ++i){ scanf("%d",&a[i]); if(a[i] > 0){ if(vis[a[i]] == 0) vis[a[i]] = 1,++cnt3; } else if(a[i] == -1) ++cnt1; else ++cnt2; } ans1 = min(cnt1 + cnt3,m); ans2 = min(cnt2 + cnt3,m); for(int i = 1; i <= m; ++i) vis[i] += vis[i-1]; for(int i = 1; i <= m; ++i) if(vis[i] - vis[i-1] > 0) ans3 = max(ans3,min(cnt1,i-1-vis[i-1]) + min(cnt2,m-i-(vis[m]-vis[i]))+cnt3); printf("%d\n",max(ans1,max(ans2,ans3))); } }
CF1824B2 LuoTianyi and the Floating Islands (Hard Version)
标签:思维题
\(B\)
我们从 Easy Version
中可以进行一个猜测,并得到结论:
- \(k \%2 = 1\) 的时候,我们可以发现答案就是 \(1\)
那么 \(k\%2 = 0\) 的时候怎么做呢?
我们其实可以枚举边,对于每条边,我们在其两边各放 \(\frac k 2\) 个点的方案数,就是我们的答案。
但是,因为我们的边数是比点数少 \(1\) 的,所以说好点的期望数是边的期望数 \(+1\)
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int NN = 2e5 + 8,MOD = 1e9 + 7; int n,k; int siz[NN]; ll ans = 0; ll fac[NN],inv[NN]; ll ksm(ll x,ll k){ ll res = 1; while(k){ if(k & 1) res = res * x % MOD; x = x * x % MOD; k >>= 1; } return res; } ll binom(ll n,ll m){ if(n < m) return 0; return fac[n] * inv[m] % MOD * inv[n-m] % MOD; } ll invb(ll n,ll m){ return inv[n] * fac[m] % MOD * fac[n-m] % MOD; } struct Edge{ int to,next; }edge[NN << 1]; int head[NN],cnt; void init(){ memset(head,-1,sizeof(head)); cnt = 1; fac[0] = 1; for(int i = 1; i <= n; ++i) fac[i] = fac[i-1] * i % MOD; inv[n] = ksm(fac[n],MOD - 2); for(int i = n; i >= 1; --i) inv[i-1] = inv[i] * i % MOD; } void add_edge(int u,int v){ edge[++cnt] = {v,head[u]}; head[u] = cnt; } void dfs(int u,int fa){ siz[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(v == fa) continue; dfs(v,u); siz[u] += siz[v]; ans = (ans + binom(siz[v],k/2) * binom(n-siz[v],k/2)) % MOD; } } int main(){ scanf("%d%d",&n,&k);init(); if(k & 1){puts("1");return 0;} for(int i = 1,u,v; i < n; ++i){ scanf("%d%d",&u,&v); add_edge(u,v);add_edge(v,u); } dfs(1,1); ans = (ans * invb(n,k) + 1) % MOD; printf("%lld",ans); }
CF1824C LuoTianyi and XOR-Tree
标签:DP
\(B\)
我们可以证明,如果需要异或,取一个点的子树内异或次数更少的一定不会更劣。
prove:
如果说在一个点处取次数更多的方案,在它的父亲处最多只会减少该点所在子树的一次异或,所以取最少的次数的方案一定不会更劣。
因为我们已经证明了每次去子树内异或次数更小的,一定不会更劣,所以我们可以用 DP
解决异或次数的问题。
我们设 \(f_u\) 表示以 \(v\) 为根的子树,最小的异或次数,\(cnts_u\) 记为 \(u\) 的儿子个数,\(maxs_u\) 记为 \(u\) 的所有儿子中出现最多的异或值的出现次数。
然而,我们最小异或次数相同的方案,显然不能直接在当前子树处理,需要将所有方案给到他的父亲,让他的父亲决定。
所以我们需要对于每个节点开一个 map
,并且使用启发式合并对所有方案进行合并。
写启发式合并的时候需要注意:
-
如果说 \(maxs\) 为 \(1\),那么我们就直接将儿子中最大的
map
和 父亲的map
交换,并且给父亲整体打一个tag
(因为此时如果去遍历最大的那个map
会使复杂度退化)。 -
如果说 \(maxs\) 大于 \(1\),那么我们就直接去暴力更新父亲的
map
(因为如果 \(maxs\) 大于 \(1\),那么这个儿子中最大的map
的大小一定是小于 \(\frac {siz_u} 2\) 的,满足启发式合并维持时间复杂度的条件)。 -
如果是叶节点,需要进行特判对
map
赋值。
最后一个小知识:swap
交换两个 STL
容器时间复杂度近似 \(O(1)\)。
code:
#include<bits/stdc++.h> using namespace std; const int NN = 2e5 + 8; #define st first #define ed second int n; int a[NN]; struct Edge{ int to,next; }edge[NN << 1]; int head[NN],cnt; int tag[NN]; int f[NN]; map<int,int> m[NN]; void init(){ memset(head,-1,sizeof(head)); cnt = 1; } void add_edge(int u,int v){ edge[++cnt] = {v,head[u]}; head[u] = cnt; } void dfs(int u,int fa){ int maxs = 0,pos = 0,cnts = 0; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(v == fa) continue; ++cnts; dfs(v,u); f[u] += f[v]; if(m[v].size() > maxs) maxs = m[v].size(),pos = v; } if(cnts == 0) return m[u][a[u]] = 1,void(0);//如果是叶节点,需要进行特判对 map 赋值 maxs = 1; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].to; if(v == fa || v == pos) continue; for(auto it : m[v]) maxs = max(maxs,m[pos][it.st ^ tag[v] ^ tag[pos]] += it.ed); } f[u] += cnts - maxs; if(maxs > 1){ for(auto it : m[pos]){//暴力更新父亲的 map if(it.ed != maxs) continue; m[u][it.st ^ a[u] ^ tag[pos]] = 1; } m[pos].clear();//防止 MLE } else swap(m[u],m[pos]),tag[u] = a[u] ^ tag[pos];//将儿子中最大的 map 和 父亲的 map 交换,并打上标记 } int main(){ scanf("%d",&n);init(); for(int i = 1; i <= n; ++i) scanf("%d",&a[i]); for(int i = 1,u,v; i < n; ++i){ scanf("%d%d",&u,&v); add_edge(u,v);add_edge(v,u); } dfs(1,1); printf("%d",f[1] + (!m[1].count(tag[1]))); }
CF1824D LuoTianyi and the Function
标签: DS
\(A^-\)
将询问差分,我们可以得到:
然后就是线段树区间加,历史版本区间和。
code:
写不动了QAQ……
本文来自博客园,作者:ricky_lin,转载请注明原文链接:https://www.cnblogs.com/rickylin/p/CF1824.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具