首页 > 其他分享 >CF1408F Two Different 题解

CF1408F Two Different 题解

时间:2024-02-07 10:16:15浏览次数:25  
标签:mathbb Different log int 题解 ans CF1408F 操作 now

解题思路

先考虑如何将一堆数变为相同的。

显然,这里有一个条件 \(n=2^k,k\in \mathbb Z\),证明如下:

  • 每次操作只能将两个数变为相同的,那么一个数在使得其他数变为相同数的操作中(我们不妨将所有数进行这种操作称为一轮操作),一个数最多被使用一次;
  • 按照错位操作,即第一轮 \(1\) 和 \(2\)、\(3\) 和 \(4\),第二轮 \(1\) 和 \(3\)、\(2\) 和 \(4\) 这种最优决策(这样可以保证每一轮结束后不同数的数量最少)每一轮也最多减少 \(\displaystyle \left\lfloor \frac{n}{2} \right\rfloor\) 种数量;
  • 按照如上方法,在第 \(i\) 轮会有 \(a_i\bmod 2^i=1\) 的数无法被操作到,那么当且仅当 \(n=2^k,k\in \mathbb Z\) 的时候,所有数可以变为相同的;

由上可以推出,当且仅当 \(n=2^k+z,k\in \mathbb Z,z\le 1\) 的时候,序列可以根据以上操作变为最多有两个不同的,而这种情况也即为样例中的情况。

那么考虑 \(n\) 为其他数的情况。我们发现对于任意大于 \(1\) 的正整数,都可以找到一个 \(x\) 使得 \(x=2^k,k\in \mathbb Z\) 且 \(\displaystyle x\ge \frac{n}{2}\)。我们还发现,因为对于任意的序列 \(a\) 和函数 \(f\),构造方案都应成立,那么可以认为 \(a\) 对于构造方案没有影响。那么我们考虑将序列拆为 \([1,x]\) 和 \([n-x+1,n]\) 两个长度均为 \(x\) 的部分,不妨先通过上文操作使得区间 \([1,x]\) 中的数全部相等,然后再通过上文操作使得区间 \([n-x+1,n]\) 中的数全部相等,由于 \([1,n-x]\) 的数在两次操作后一定是相等的,而 \([n-x+1,n]\) 中的数在两次操作后是相等的,那么在这种操作方案下,最终序列一定有小于等于两种不同的数。

由于我们在使一个序列相同时错位操作,那么每个数在一轮中会被恰好操作一次,又由于每轮操作会使不同的数的数量减半,那么最多进行 \(O(\log_2x)\) 轮操作,那么我们最终总共会进行 \(O(2\cdot x\log_2x)\) 次操作,而在 \(n\le 1.5\times 10^4\) 的数据范围下,操作次数是小于等于 \(229376\) 次的,可以通过本题。

时间复杂度 \(O(n\log n)\) ,空间 \(O(n\log n)\),可以通过本题。

注意事项

错位操作时细节有亿点多,如果是循环写法,一定要注意倍增的时候不要超出 \(n\) 的范围了;如果是递归或 vector 合并写法,注意数组的清空。

AC 代码

#include<stdio.h>
#include<stdlib.h>
#include<vector>
#define N 15005
#define pii std::pair<int,int>
int n,cnt,c[N];
std::vector<pii> ans;
std::vector<int> a[N],b[N];
inline void print(){
   printf("%d\n",ans.size());
   for(auto now:ans){
       int x=now.first;
       int y=now.second;
       printf("%d %d\n",x,y);
   }
}
inline void GetAns(int l,int r){
   int m=r-l+1,now=2;while(m>1){
       for(register int i=l;i<=r;i+=now)
       for(register int j=i;j<i+(now>>1);++j)
           ans.push_back({j,j+(now>>1)});
       m>>=1;now<<=1;
   }
}
signed main(){
   scanf("%d",&n);
   for(register int i=1;i<=n;++i)
       c[i]=i;int mid=1;
   while((mid<<1)<n) mid<<=1;
   GetAns(1,mid);
   GetAns(n-mid+1,n);
   print();
}

标签:mathbb,Different,log,int,题解,ans,CF1408F,操作,now
From: https://www.cnblogs.com/UncleSamDied6/p/18010658

相关文章

  • CF1428D Bouncing Boomerangs 题解
    解题思路很简单的贪心。观察发现以下性质:当\(a_i=2\)时,这一行一定只有两个目标,且第二个目标一定位于一个\(a_j=1\)的格子内;当\(a_i=3\),那么当前列右边某一列发生转向的地方,\(a_j\not=0\);那么这道题就基本已经做出来了。因为\(a_i=3\)的格子转向格可以在任意非\(0\)......
  • CF1401E Divide Square 题解
    解题思路其实多看几组也能发现块数等于交点的数量加上两个端点都在边上的线段数量再加一。证明如下(图见样例):对于两条只有一个端点位于边上的线段,因为保证有一个端点位于边上,那么这两条线段的交点一定会和已存在的点、边构成一个新的矩形;对于其中有一条为两个端点均位于边上的......
  • CF1401F Reverse and Swap 题解
    解题思路巧妙的数据结构题,非常巧妙的利用的线段树的奇妙性质。操作逐条维护:Replace:直接线段树上单点修改;Sum:线段树查询区间和;Reverse:考虑线段树的形态。线段树第\(i\)层(除最后一层)有\(2^{i-1}\)个节点,那么将所有\(i\ge1\)的区间\([(i-1)\times2^k,i\times2^k]\)......
  • CF1408E Avoid Rainbow Cycles 题解
    解题思路第一眼看过去感觉不是很可做……但是我们可以发现,如果有两个点在不同的集合中出现过,那么一定会存在彩虹环,那么两个点最多出现一次。同时我们考虑将题意转化一下,变成求最大能选取的点,使得不出现彩虹环。根据刚刚的性质,我们可以考虑每个点向它所在的集合连一条边权为\(a_......
  • CF1338C Perfect Triples 题解
    解题思路没什么好说的,就是打表找规律……表在这里不难发现,三元组中第一个数的最后两位按照\(00\to01\to10\to11\)的顺序变化,其他位也一样,同样,第二个数和第三个数中每两位分别按照\(00\to10\to11\to01\)和\(00\to11\to01\to10\)的顺序变化,且与第一个数对应变化......
  • CF1379C Choosing flowers 题解
    解题思路不是那么显然的,当只选一种\(b_i\)或全选\(a_i\)时最优。那么我们可以先对\(a_i\)从大到小排序,枚举每一个\(b_i\),然后二分找到第一个大于等于\(b_i\)的\(a_j\),判断\(a_1\sima_j\)中是否包含\(a_i\),如果包含,当前的答案为\(\displaystyle\left(\sum_{k=1}^{......
  • CF1385F Removing Leaves 题解
    解题思路简单贪心,优先选择叶子节点最多的,这样能够保证一定能把所有能删的都删了。因为要建一个可删除的图,所以我们可以使用set来存边,不然就需要维护一堆东西……那么我们肯定是从有叶子节点的点向父亲更新的,那么我们每次选择叶子节点最多的点,然后删除\(k\)个叶子,判断一下删......
  • CF1415E New Game Plus! 题解
    解题思路简单贪心题,我们可以把整个序列看作\(k+1\)个可重集,首先可以得到一个显然的结论:较大的数一定比较小的数先放入一个集合中。同样,由于每一轮\(ans\getsans+\maxsum_i\),其中\(sum_i\)表示第\(i\)个集合的元素和,那么,我们一定会将当前的元素放入当前和最大的哪个集合......
  • CF1416B Make Them Equal 题解
    解题思路观察可以发现,每次操作后序列元素之和不变,那么我们可以将每一次操作看作是\(a_i\)向\(a_j\)移动了\(i\timesx\)。由此可得,若序列和\(sum\bmodn\not=0\),那么一定无解,否则一定存在一个合法的操作方案。因为每次移动时移动的数应为\(i\)的倍数,所以\(a_1\)可以向......
  • CF1446C Xor Tree 题解
    解题思路与其考虑删除哪些点,不如考虑保留哪些点。考虑到和异或有关,那么我们可以把这些数倒序插入trie树中,然后我们就可以在trie树上跑一个简单的dp:若当前节点为叶子节点,那么保留,返回\(1\);若当前节点在链上,那么直接继承儿子节点;若当前节点有两个儿子,那么更新为较大儿子......