1 条题解

  • 0
    @ 2025-8-24 23:01:01

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar RainBow_doge
    醒了

    搬运于2025-08-24 23:01:01,当前版本为作者最后更新于2024-07-13 19:10:09,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目大意

    有一个 1n1\sim n 的序列,共有 kk 次操作,11 操作指将下标为奇数的数字删除,22 操作指将下标为偶数的数字删除,最后输出删除后的最后一个数字。

    分析

    看到数据范围为 1n10181\le n\le10 ^{18},知道肯定不能开数组暴力过,那就开始找规律:

    首先我们要明白最后得出的数字到底在哪个位置。

    我们可以举几个例子

    88 33 22 22 2 2

    1 2 3 4 5 6 7 8
    (1)1 3 5 7
    (2)1 5
    (3)1
    

    77 33 22 22 22

    1 2 3 4 5 6 7
    (1)1 3 5 7
    (2)1 5
    (3)1
    

    1111 44 22 22 22 22

    1 2 3 4 5 6 7 8 9 10 11
    (1)1 3 5 7 9 11
    (2)1 5 9 11
    (3)1 9
    (4)1
    

    这些得数有什么共同点吗?

    • 他们都只进行了 22 操作。

    • 他们得数都为 11

    • 每进行一次 22 操作,相邻两数间的差值就会变大,且每次变大后的差值为变化前相邻两数差值的 22 倍。

    即第 ii 次操作,两数间的差值为 2i2^i

    那为什么他们的结果都为 11 呢?

    我们可以思考一下:

    22 操作每次是把偶数位删除,因此每次操作基本所有数字都会转移位置,但有一个数除外,即 11 号位置上的数,他的前面没有数字了,因此他并不会向前补,并且因为他是奇数点,所以他不会被删除,那么他的位置就不会发生变化。

    因为一号位置上的数的位置不会改变,而且 22 操作并不会删除奇数点,所以一号位置上的数字在 22 操作下将一定不会被删去,所以一号位置上的数一定为结果。

    那我们就能得出:真正的结果不是 11,而是 11 号位置上的数。

    因此我们在代码中若遇见 22 操作,只用更改两点之间的差值,而不用考虑结果。

    if(x==2){//x为操作编号
      cnt*=2;//cnt为两点之间的差值
    }
    

    那说了这么久的 22 操作,11 操作要怎么办呢?

    我们再举几个例子

    我们在原有 22 操作中插入几个 11 操作。

    77 33 11 22 22

    1 2 3 4 5 6 7
    (1)2 4 6
    (2)2 6
    (3)2
    

    88 33 11 11 22

    1 2 3 4 5 6 7 8
    (1)2 4 6 8
    (2)4 8
    (3)4
    

    貌似看不出什么共同点,但我们可以联系上面的 22 操作。

    我们发现,尽管 11 操作将第一个位置上的数删除了,但第一个位置的数后面的一个数成为了第一个位置上的数,也就是将第二个数作为了答案。

    那第二个数要如何求得呢?

    这时候我们就用到了操作 22 中求到的差值了,因为求的差值为相邻两数之间的差,那么第二个数就可以理解为第一个数加上差值。

    而且 11 操作因为跳着删除了一些数字,所以他也需要与 22 操作一样变更差值,差值也是一样变成 2i2^i

    综上所述,11 操作就比 22 操作多了一步要更改第一个位置上的数的值,那么 11 操作就可以变为 22 操作,只不过要更改一下答案。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long//10^18一定要开long long 
    int main(){
    	int t;
    	cin>>t;
    	while(t--){
    		ll n,k;
    		cin>>n>>k;
    		ll first=1;//first为第一个位置上的数,也就是本题的答案 
    		ll cnt=1;//cnt为相邻两数的差值,一开始为1 
    		for(int i=1;i<=k;i++){
    			int x;
    			cin>>x;
    			if(x==1){
    				first+=cnt;//执行1操作后第一个数变为第二个数,也就是原firt加上差值 
    				cnt*=2;//重新更新两数差值 
    			}
    			if(x==2)
    				cnt*=2;//只需要更新两数的差值 
    		}
    		cout<<first<<endl;//第一个位置的数一定是最后一个数,也就是答案
    	}
    	return 0;//完结撒花! 
    }
    
    • 1

    信息

    ID
    10159
    时间
    1000ms
    内存
    512MiB
    难度
    2
    标签
    递交数
    0
    已通过
    0
    上传者