CF 1800~2100 思维题泛做

1648B

1800
关键词:数学、算法优化

1648B Integral Array

给定长为 n 满足 max1in aic 的序列 a
求问是否能够保证序列 a 中所有满足 xy 的两数,xy 也在序列中。

 n, c106,n,c106.

  • O(n2) 的算法

看到题目就能一眼出的算法,无非暴力枚举所有 x,y 判断。
因为有 c106,可以开一个桶存一个数是否出现过,这样就能做到 O(1) 判断。
时间复杂度 O(n2)

  • O(nn) 的算法

把直接枚举序列中的每个数,改成对于数 x,枚举 [2,n] 区间内的数。
时间复杂度降至 O(nn),但还需优化。

#define L(i,j,k) for(int i=(j);i<=(k);i++)
void work(){
  cin>>n>>k;L(i,1,k) t[i]=0;
  L(i,1,n) cin>>a[i],t[a[i]]=1;
  if(!t[1]) return cout<<"No"<<endl,void();
  L(i,1,n) L(j,2,(sqrt(a[i])))
    if(!(t[j]+t[a[i]/j]^1))
      return cout<<"No"<<endl,void();
  cout<<"Yes"<<endl;
}
  • O(clnc) 的算法

正片开始!
如果枚举因数不行,那就枚举倍数。
先对序列去重,特判序列中不存在 1 的情况直接判负。
对于 x[i×j,i×(j+1)),也就是 xi=j 的情况,
如果 x,ia,ja,则此序列不满足条件。
可以直接在 [1,c] 的范围内枚举 i 和相应的 j 并判断。
上面的式子可以想到用之前处理出桶数组进行前缀和 O(1) 判断。
时间复杂度 O(clnc),详见调和级数——百度百科
提交记录

1583D

1800
关键词:交互、拆分任务

1583D Omkar and the Meaning of Life

存在一个长度为 n 的排列 a,最多询问 2×n 次,求出排列 a
每次询问提交一个序列 b,会回复序列 a,b 之和所得序列中第一个出现不止一次的数的位置,没有则返回 0

n100

从询问 2×n 的条件上看,可以想到将其拆成 n+n 次询问,分别实现不同的目标。
在这道题里,是从局部推至整体。

  • 局部:

很明显,我们可以在 n 次询问内求得任意一个排列 a 中的数值。
除了要求的位置,序列 b 全赋值为 1,所求位的值从 n 开始递减,假设值为 k 时返回值不为 0,那么排列 a 当前位的值就是 nk+1
根据题目中第一个出现不止一次的数的位置的描述,先求 an 可以避免位置的干扰。

  • 整体:

只要我们知道了 an,那就可以用 n1 次询问求得排列 a 其余所有元素。
因为排列中每个数唯一,考虑将 an 补至 n+1,就能保证询问提交序列的 max1inbin
要求得数的位置必然在 an 之前,所以正确性也能保证。

#include <bits/stdc++.h>
using namespace std;
#define L(i,j,k) for(int i=(j);i<=(k);i++)
#define R(i,j,k) for(int i=(j);i>=(k);i--)
int a[200],ans[200],k=1,n,x;
void ask(){
  cout<<"? ";L(j,1,n) 
    cout<<a[j]<<" \n"[j==n];
  cout.flush();cin>>x;
}signed main(){
  // freopen("test.in","r",stdin);
  cin>>n;L(i,1,n-1) a[i]=1;
  R(i,n,1){a[n]=i;ask();if(x){k=i;break;} }
  ans[n]=n-k+1;a[n]=n+1-ans[n];
  L(i,1,n){
    L(j,1,n-1) a[j]=n+1-i;
    ask();ans[x]=i;
  }cout<<"! ";L(i,1,n) 
    cout<<ans[i]<<" \n"[i==n];
  cout.flush();
}

提交记录

询问次数 O(n) 的做法:

设询问的数组为 A,最终答案数组 B
跟之前的做法类似,设 A=[1,1,,1,k],但是 k2,3, 开始枚举。
当第一次的返回为 0 时,就可以确定 Bn=nk+1
这一过程中返回的非 0 信息其实也能利用。
如果当 k=x 时询问的回复是 u,那么很容易推得 Bu=Bn+x1
这样就能在 nBn 次询问后,确定值在 [Bn,n] 范围内的数的位置。
在剩下的 bn1 次询问中,先将 An 设为 1,其余 B 中确定的值在 A 中对应的位置都设为 n
总共循环 bn1 次,在第 i 次时,将 A 中所有不确定的位置全设为 i+1,询问返回 u,则可以确定 Bu=Bni
这样,我们实现了在询问次数为 O(n) 的情况下解决了这个问题。
提交记录

1666C

1800
关键词:构造

1666C Connect the Points

给定二维平面上 3 个点,用总长度最短的线段连接这 3 个点。求线段数和每条线段的端点。

109xi,yi109.

构造题,可以想到最短的线段长度max1i3ximin1i3xi+max1i3yimin1i3yi
将点按照 x 轴坐标排序,两边的点向中间连边,中间点过平行于 y 轴,包括三个点的线段。
如下图所示:

-----|
     |
     |-----
     |

线段数恒为 3

#include <bits/stdc++.h>
#define F(i) a[i].first
#define S(i) a[i].second
using namespace std;
pair <int,int> a[5];
int main(){
  freopen("test.in","r",stdin);
  for(int i=1;i<=3;i++) cin>>F(i)>>S(i);sort(a+1,a+4);
  cout<<3<<'\n'<<F(1)<<' '<<S(1)<<' '<<F(2)<<' '<<S(1)<<'\n';
  cout<<F(2)<<' '<<min(S(1),min(S(2),S(3)))<<' '<<F(2)<<' '<<max(S(1),max(S(2),S(3)))<<'\n';
  cout<<F(2)<<' '<<S(3)<<' '<<F(3)<<' '<<S(3);return 0;
}

提交记录

1583D

1900

CF1583D Tournament Countdown

关键词:交互、转化信息、读题

因为连着读错两次题导致几乎荒废了一整个下午。
其实还是比较容易知道,只要在两次询问中排除三个人就能保证在规定次数内求解。
其实题目已经限定的很死了,整个框架已经搭好。
如果只是两个人,比一下就行。
如果是四个人,就要考虑如何两次询问排除。

比如 1,2,3,4 四人。
头脑风暴可知,先比较 1,3,根据结果分类讨论。

  • 回复 0

此时 1,3 都是 0 场,比较 2,4 即可。

  • 回复 1

此时 1 必定赢 23 必定输 4,比较 1,4

  • 回复 2

和上面差不多推理,比较 2,3

如此实现了两次询问排除三人。
重复上述过程即可。
如果最后剩下两人就再比一次。
为什么读错题还是两次一起啊喂

int n;int ask(int x,int y){
  cout<<"? "<<x<<' '<<y<<'\n';
  cl;int g;cin>>g;return g;
}void out(int x){cout<<"! "<<x<<'\n';cl;}
void work(){
  cin>>n;vi a(1<<n);iota(all(a),1);
  while(a.size()>=4){
    vi b;for(int i=0;i+3<a.size();i+=4){
      int x=ask(a[i],a[i+2]);
      if(!x) b.pb(ask(a[i+1],a[i+3])==1?a[i+1]:a[i+3]);
      else if(x==1) b.pb(ask(a[i],a[i+3])==1?a[i]:a[i+3]);
      else b.pb(ask(a[i+1],a[i+2])==1?a[i+1]:a[i+2]);
    }a=b;
  }out(a[0]=(a.size()==2)?a[ask(a[0],a[1])-1]:a[0]);
}

提交记录

1701D

1900

CF1701D Permutation Restoration

关键词:贪心,优先队列

首先,根据题目给出的式子我们可以反向算出 ai 的取值范围:[ibi+1+1,ibi]

现在题目转化成了,每个点对应有若干个线段。
采取贪心策略。

对于一个点,将所有以这个点为左端点的线段存入对应的数组。
之后进行贪心,对于每个点,将其对应的线段压入优先队列,每次选取右端点最小的线段。
因为选取右端点最小,使得之后的选择余地更大。

最后注意下 bi=0 的情况,不能直接除,直接赋值为 n 即可。

#define pb push_back
#define vi vector<int>
#define pi pair<int,int>
int n;priority_queue<pi>q;
const int N=5e5+100;
int a[N],b[N];
vi p[N];pi t[N];
void work(){
  cin>>n;L(i,1,n) p[i].clear();
  while(!q.empty()) q.pop();
  L(i,1,n) cin>>a[i];
  L(i,1,n){
    t[i]={i/(a[i]+1)+1,a[i]?i/a[i]:n};
    p[F(t[i])].pb(i);
  }L(i,1,n){
    for(int x:p[i]) q.push({-S(t[x]),x});
    int x=S(q.top());q.pop();a[x]=i;
  }L(i,1,n) cout<<a[i]<<" \n"[i==n];
}

提交记录

1485D

2200
关键词:人类智慧

CF1485D Multiples and Power Differences

题目中有个重要信息:1ai,j16,限制的如此之死,绝非偶然之举。
因为 lcm(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)=720720,在题目给出的 106 范围内,所以可以进行构造。
进行黑白染色,区分是加上 ai,j4 还是不变即可。
时间复杂度:O(nm)
空间复杂度:O(1)

#define L(i,k) for(int i=1;i<=(k);i++)
void work(){
  int n,m,x;cin>>n>>m;L(i,n) L(j,m)
    cin>>x,cout<<720720+(((i+j)&1)?0:pow(x,4))<<" \n"[j==m];
}

纯粹就是思路特阴间,代码特好写的诈骗构造题。
所以才记录这样一道 2200 的题目

提交记录

posted @   AIskeleton  阅读(78)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示