发现已经错过最美的花期|

ricky_lin

园龄:3年8个月粉丝:11关注:2

【题解】CF1854 合集

CF1854A2 Dual (Hard Version)

你考虑我们 A1 只需要通过自加凑一个最大的数,然后将所有的数都变成正数,最后做一次前缀和即可。(不懂可以看看落谷题解)

好,我们现在去看 Hard Version\(31\) 次操作怎么分配:

  • 前缀和(全为正)/ 后缀和 (全为负)—— \(19\)

  • 还剩下 \(12\) 次,不知道该怎么做。

我们的目标便变为了:在 \(12\) 次之内让所有数变成全为正/全为负

现在,我们需要整理一下我们手中的方法:

  • 我们 Easy Version 中是怎么做的呢,我们找了一个数,然后让它变成极大/极小,这一步在最劣情况下显然是 \(5\) 次 (\(1\to2\to4\to8\to16\to32\)),然后所有数都要变一遍 —— \(5+n\)
  • 当然,我们也有一种方法就是说找一个绝对值最大的数,然后让它给所有数加一遍,让其他的数和它符号相同 —— \(n\)

当然上面的 \(n\) 其实是上限,准确来说应该将 \(n\) 替换为和选出的数异号的数的个数。

那么,我们就可以将两种方法结合一下。

  • 我们先找到一个绝对值最大的数 \(x\),然后计算和这个数符号相同的数的个数(包括 \(0\)
  • 如果同号的数的个数 \(\geq 7\)(异号的数的个数 \(\leq 12\)),那么就直接让这个这些数都加上 \(x\)\(\leq 12~times\)
  • 否则,就造一个和 \(x\) 异号的绝对值极大的数,然后将 \(x\) 和与 \(x\) 同号的数都加上这个数。(\(\leq 5 + 6 + 1~times\)

最后将序列变为原序列的前/后缀和序列即可.

code:

#include<bits/stdc++.h>
using namespace std;
const int NN = 40;
int t,n;
int a[NN];
int maxn,pos;
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
maxn = 0,pos = 0;
for(int i = 1; i <= n; ++i){
scanf("%d",&a[i]);
if(abs(a[i]) > maxn) maxn = abs(a[i]), pos = i;
}
if(maxn == 0){puts("0");continue;}
int cnt = 0;
for(int i = 1; i <= n; ++i){
if(a[i] * a[pos] >= 0 && i != pos) ++cnt;
}
if(cnt == n-1){
printf("%d\n",n-1);
}
else if(cnt < 7){
printf("%d\n",5 + cnt + n);
for(int i = 1; i <= n; ++i)
if(a[i] * a[pos] < 0){pos = i;break;}
for(int i = 1; i <= 5; ++i) printf("%d %d\n",pos,pos);
for(int i = 1; i <= n; ++i)
if(a[i] * a[pos] <= 0 && i != pos) printf("%d %d\n",i,pos);
}
else{
printf("%d\n",n - cnt - 1 + n - 1);
for(int i = 1; i <= n; ++i){
if(a[i] * a[pos] < 0 && i != pos) printf("%d %d\n",i,pos);
}
}
if(a[pos] > 0) for(int i = 1; i < n; ++i) printf("%d %d\n",i+1,i);
else for(int i = n; i > 1; --i) printf("%d %d\n",i-1,i);
}
}

CF1854B Earn or Unlock

标签:DP \(B\) | 杂项 \(C\)

你考虑,我们很容易地可以构造一个 \(n^2\) 状态的 DP

  • \(f_{i,j}\) 表示当前在 \(i\) 张牌,还可以摸 \(j\) 张牌的最大分数。转移也很好转移,你考虑一眼就会。

但是我们显然要缩减复杂度,我们看到数据范围 \(10^5\),想到了根号。

分块???显然不行。莫队???都没有区间查询,怎么行呢?

然后你苦思冥想,到最后也没有想出来这道题。

你考虑其实我们可以把 \(j\) 这一维吃掉,如果我们将继续往下摸牌少去的牌的代价均摊到后面的 \(a_i\) 张牌上,那么我们知道一个 \(i\),即可求出在该点的分数。

即对于位置 \(i\),对应的分数为 \(\sum\limits_{j = 1}^n a_j - i + 1\)

然后现在我们的要求就变成了看到底能不能在位置 \(i\) 摸到完最后一张牌。

我们可以发现这个转移是 \(n^2\) 的,但是因为状态只有 \(0/1\) 所以说可以使用 bitset 进行优化,让复杂度变为 \(O(\frac {n^2} \omega)\)

处理的时候还有一个细节,就是计算的时候,摸牌的最后一个位置可能会到达 \(2\times n\),我们只需要将 \(a_{n+1\sim2n}\) 都赋值为 \(0\) 即可。

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll NN = 2e5 + 8;
ll n,m,ans;
ll a[NN],pre[NN];
bool f[NN];
bitset<NN> dp;
int main(){
scanf("%lld",&n);
pre[0] = 0;dp[1] = 1;
for(int i = 1; i <= n; ++i)
scanf("%lld",&a[i]), pre[i] = pre[i-1] + a[i];
for(int i = 1; i <= n; ++i){
dp = (dp | (dp << a[i]));
f[i] = dp[i];dp[i] = 0;
}
for(int i = n + 1; i <= 2 * n; ++i) {
f[i] = dp[i];
pre[i] = pre[i - 1];
}
ans = 0;
for(int i = 1; i <= 2 * n; ++i) if(f[i]) ans = max(ans, pre[i] - i + 1);
printf("%lld", ans);
return 0;
}

CF1854C Expected Destruction

标签:DP \(B^+\) | 数学 \(C^+\)

你考虑,我们如果没有重合就将元素删去的操作,我们就有答案:\(n \times (m+1) - \sum\limits_{i=1}^n a_i\)

但是,我们显然最后的答案是小于这个的,如果有两个数在 \(i\) 相撞,那么我们的答案就会减少 \((m-i+1)\)

我们设 \(f_{i,j}\) 表示两个数分别在 \(i\)\(j\) 的概率 \((i\leq j)\)\(f_{i,i}\) 表示第一次相撞在 \(i\) 的概率,我们可以得到下面递推式:

\[f_{i,j} = f_{i,j-1} \times \frac {[i \neq j-1]} 2 + f_{i-1,j} \times \frac {[i-1 \neq j]} 2 \]

然后我们最终的答案即为:

\[n \times (m+1) - \sum\limits_{i=1}^n (a_i + f_{i,i} \times (m-i+1)) \]

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 508,MOD = 1e9 + 7;
bool Med;
int n,m;
ll ans;
ll s[NN];
ll f[NN][NN];
bool Mbe;
int main(){
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; ++i) {
scanf("%lld",&s[i]);
ans = (ans + m + 1 - s[i]) % MOD;
if(i != 1) f[s[i-1]][s[i]] = 1;
}
for(int i = 1; i <= m; ++i){
ans = (ans + MOD - 1ll * f[i][i] * (m - i + 1) % MOD) % MOD;
for(int j = i + 1; j <= m; ++j){
f[i][j+1] = (f[i][j+1] + f[i][j] * (MOD+1 >> 1) % MOD) % MOD;
f[i+1][j] = (f[i+1][j] + f[i][j] * (MOD+1 >> 1) % MOD) % MOD;
}
}
printf("%lld\n",ans);
// fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
// fprintf(stderr, "%.5lf ms\n", 1e3 * clock() / CLOCKS_PER_SEC);
}

CF1854D Michael and Hotel

标签:思维题 \(B^+\)

交互题。

考虑题意即为找到 \(1\) 所在内向基环树上的所有点。

我们考虑我们怎么找到环上的点,我们考虑我们可以 \(O(\log n)\) 询问到一个环上的点,方法即为将 \(k\) 定为一个大数,然后二分点集。然后我们便可以在 \(O(n\log n)\) 的时间复杂度内找到所有环上的点(我们一会儿再讲怎样优化)。

我们找到环上的点了之后,我们再将 \(S\) 设为环上的点,\(k\) 设为一个极大数,\(u\)\(1\sim n\) 遍历,如果返回 \(1\) 即为可以到达,否则不能到达,这样我们就可以在 \(O(n)\) 的时间内找到所有链上的点。

我们显然现在需要将找环上的点的操作再精简一下。

我们考虑我们找链上的点时的方法十分地高效,我们可以略作修改来找环。

我们考虑假设我们找到了环上的长度为 \(len\) 的链,那么我们可以将 \(S\) 设为已经找到的环上的点,\(k = len\)\(u\)\(1\sim n\) 遍历。返回 \(1\) 便将点加入环,这样,我们每次的环长就可以倍增了,停止条件就是环长没有倍增(即绕了一圈回来了)。

上面的方法显然可能将环外的点也加进来,但是显然没有任何印象。

我们便可以将两种找环上点的方法结合起来,先每次 \(\log n\) 询问环上的单点0,然后每次 \(O(n)\) 倍增环上的点。由下图可知,一开始询问出长度为 \(56\) 链是最优的,当然上下浮动一点点也无伤大雅。

image

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 5e2 + 8;
int n,tot,rt = 1;
bool check(int tim,int l,int r){
printf("? %d %d %d ",rt,tim,r - l + 1);
for(int i = l; i <= r; ++i) printf("%d ",i);
puts("");fflush(stdout);
int op;scanf("%d",&op);
return op;
}
set<int> Nodes;
bool vis[NN];
void Get_Part_Of_Ring(){
int l = 1,r = n;
while(l < r){
int mid = (l + r) / 2;
if(check(1000,l,mid)) r = mid;
else l = mid + 1;
}
Nodes.insert(l);
rt = l;
for(int i = 1; i <= 62; ++i){
l = 1; r = n;
while(l < r){
int mid = (l + r) / 2;
if(check(1,l,mid)) r = mid;
else l = mid + 1;
}
if(vis[l]) break;
vis[l] = 1;
Nodes.insert(l);
rt = l;
}
}
void Get_Ring(){//事实上应该包括了一部分的链
if(Nodes.size() != 63) return;//环找完了,在上一个函数里面
int sz = Nodes.size();
while(1){
tot = 0;
for(int i = 1; i <= n; ++i) if(!vis[i]){
printf("? %d %d %d ",i,sz,Nodes.size());
for(auto j : Nodes) printf("%d ",j);
puts("");fflush(stdout);
int op;scanf("%d",&op);
if(op) vis[i] = 1,Nodes.insert(i);
}//将环扩倍,当然也会有环外面链的部分被包含进来,但是无伤大雅
sz *= 2;
if(sz > Nodes.size()) break;//有一部分重复了,环长没有扩倍,说明环找完了
}
}
void Get_Link(){
for(int i = 1;i <= n;i++)if(!vis[i]){
printf("? %d %d %d ",i,1000,Nodes.size());;
for(auto j : Nodes) printf("%d ",j);
puts("");fflush(stdout);
int op;scanf("%d",&op);
if(op == 1) Nodes.insert(i);
}
}
void print(){
printf("! %d ",Nodes.size());
for(auto i : Nodes) printf("%d ",i);
puts("");fflush(stdout);
}
int main(){
scanf("%d",&n);
Get_Part_Of_Ring();
Get_Ring();
Get_Link();
print();
return 0;
}

CF1854E Game Bundles

标签:思维题 \(A\)

你考虑我们需要构造出一组解,显然地这样的解有很多很多种(\({60^{60}}\) 显然是及其地大)。

那关键是我们如何进行构造。

我们很容易知道每个集合里面 \(> 30\) 的数只有一个。

所以我们可以在 \([1,30]\) 中随机 \(a_i\),直到满足的组数恰好小于等于 \(a_i\),添加的时候维护数组 \(f_i\) 表示和为 \(i\) 的集合 \(S\) 的个数。

我们可以发现,有些时候小的 \(a_i\) 如果很多,那么我们的答案增涨速度会很快,所以我们可以进行一个优化,每次随机一个 \(len\),然后让 \(a_i\)\([1,len]\) 中随机。

然后我们最后差的答案怎么补上呢?我们显然可以在序列中添加 \(> 30\) 的数,添加 \(i\) 这个数,显然会让 \(f_{60} = f_{60} + f_{60-i}\) 然后我们按 \(f_{60-i}\) 从大到小加入 \(i\) 这个数即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int NN = 2e5 + 8;
mt19937 rnd(time(0));
int T,n,m;
ll a[NN],b[NN],p[NN];
ll f[NN];
int rdbt(int l,int r){return l + rnd() % (r-l+1);}
ll K,res;
int main(){
scanf("%lld",&K);
if(K <= 60){
printf("%lld\n",K);
for(int i = 1; i <= K; ++i) printf("60 ");
puts("");
return 0;
}
for(int i = 31; i <= 60; ++i) p[i] = i;
while(1){
for(int i = f[0] = 1; i <= 60; ++i) f[i] = 0;
int k = rdbt(1,29);
for(int i = 1; i <= n; ++i){
a[i] = rdbt(1,k);
if(f[60] + f[60-a[i]] > K){n=i-1;break;}
for(int j = 60; j >= a[i]; --j) f[j] += f[j-a[i]];
}
res = K - f[60];
sort(p+31 , p+61,[&](int x,int y){
return f[60-x]>f[60-y];
});
if(res < 0) assert(0);
for(int i = 31; i <= 60; ++i)
while(n < 60 && res >= f[60-p[i]]) res -= f[60-p[i]], a[++n] = p[i];
if(!res){
printf("%d\n",n);
for(int i = 1; i <= n; ++i) printf("%d ",a[i]);
puts("");
return 0;
}
}
}
posted @   ricky_lin  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 有我 周深
有我 - 周深
00:00 / 00:00
An audio error has occurred.

作词 : 唐恬/闫光宇

作曲 : 钱雷

编曲 : 赵兆/付虹宇

制作人 : 赵兆

出品 : 共青团中央宣传部

版权 : 中国青少年新媒体协会

制作单位 : 能量悦动音乐

发行单位 : 银河方舟StarNation

出品人 : 郭峰

总监制 : 汤杰

总策划 : 钟亚楠

总统筹 : 金慧子

音乐监制 : 李天鹏/李三木

制作执行 : 张不贰/高聪怡

项目宣发 : 肖健/张国党/孙小千/戴胤/孙雯璟

音乐推广 : 代诗琪/杜思潮/马越/程铁峰/傅之豪

钢琴 : 赵兆

吉他 : 伍凌枫

贝斯 : 韩阳

鼓 : 武勇恒

合唱设计 : 赵兆

合唱 : 凡尔赛合唱团

人声录音 : 耿潇微

人声录音室 : 55TEC Studio Beijing

配唱 : 徐威@52Hz Studio (Shanghai)

混音 : 李游(小骷髅)@55TEC Studio Beijing

海报 : 格子

特别鸣谢 : 周深工作室

世界问 你是谁 来自哪 请回答

爱什么 梦什么 去何方 请回答

答案有 一百年的时光

我来自 硝烟中 课桌旁 的太阳

我来自 硝烟中 课桌旁 的太阳

他和她 宣的誓 迎的仗

来自那 燃烧的 和我一样 的年华

来自世间 一对平凡的夫妻 身旁

来自世间 一对平凡的夫妻 身旁

来自昨天 谁以青春赴万丈 理想

我是寸土 不让的 家乡啊

我是绝不 低头的 倔强啊

接过万千热血 的初衷

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去何方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

我的名字就是 站立的地方

Wu~

我的样子 就是 明天的模样

我是朝阳 落在乡间听书声 朗朗

我是朝阳 落在乡间听书声 朗朗

我是屏障 为谁挡一程厄运 的墙

我要一生 清澈地 爱着啊

我要长歌 领着风 踏着浪

朝着星辰大海 的方向

当有对答世界 的音量

要怎么形容明天 像我一样

要怎么形容明天 像我一样

承风骨亦有锋芒 有梦则刚

去远方 去最高 的想象

前往皓月星辰 初心不忘

那未来如何登场 有我担当

那未来如何登场 有我担当

定是你只能叫好 那种辉光

护身旁 战远方 有我啊

一生骄傲为我 站立的地方

Wu~

我的样子 就是 中国的模样

Wu~~~ Wu~~~

当炬火 去化作那道光

“谨以此歌献给一代代不负时代重托的中国青年”

“谨以此歌献给一代代不负时代重托的中国青年”