• 题解 P6148 【[USACO20FEB]Swapity Swapity Swap S】


    前言

    考场上没想到用倍增,呜呜呜~,只写了个找循环节,然后就 3030 分。

    正文

    分析

    考虑用倍增,其实这道题和这道题是有异曲同工之处的。

    我们 fijf_{ij} 记录第 jj 个元素,经过 2i2^i 次翻转后,这个元素的值。

    f0,jf_{0,j}

    好,那么显然,我们要先求出 f0,jf_{0,j}

    read(n);read(m);read(k);//读入
    for(int i=1;i<=m;i++)read(a[i]),read(b[i]);//读入
    for(int i=1;i<=n;i++)c[i]=i;//给c数组赋初值
    for(int i=1;i<=m;i++)reverse(c+a[i],c+b[i]+1);//模拟
    for(int i=1;i<=n;i++)f[0][i]=c[i];//经过1次翻转第i个元素的值为c[i]
    

    写倍增

    因为 2i=2i1+2i12^i=2^{i-1}+2^{i-1}

    所以,fi,j=fi1,fi1,jf_{i,j}=f_{i-1,f_{i-1,j}}

    给第 jj 个元素操作 2i12^{i-1} 次,再操作 2i12^{i-1} 次,就相当于直接操作 2i2^i 次。

    学过 LCALCA 的应该都会。

    for(int i=1;i<=30;i++)
    	for(int j=1;j<=n;j++)
    		f[i][j]=f[i-1][f[i-1][j]];//就是之前的公式
    

    得到答案

    我们知道,任何一个十进制整数都是可以转成二进制形式

    这里的话,我们就拆分 kk。这里的步骤也很像 LCALCA

    for(int i=1;i<=n;i++){
    	int x=i,m=k;
    	for(int j=30;j>=0;j--)
    		if(m>=(1ll<<j)){
    			m-=(1ll<<j);//拆
    			x=f[j][x];//操作
    		}
    	writen(x);//输出
    }
    

    复杂度

    这个复杂度显然是 O(nlogk)O(n log k) 是一个不错的复杂度。

    总代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    template<typename T>inline void read(T &FF){
    	T RR=1;FF=0;char CH=getchar();
    	for(;!isdigit(CH);CH=getchar())if(CH=='-')RR=-1;
    	for(;isdigit(CH);CH=getchar())FF=(FF<<1)+(FF<<3)+(CH^48);
    	FF*=RR;
    }
    template<typename T>inline void write(T x){
    	if(x<0)putchar('-'),x*=-1;
    	if(x>9)write(x/10);
    	putchar(x%10+48);
    }
    template<typename T>inline void writen(T x){
    	write(x);
    	puts("");
    }
    const int MAXM=1e2+10,MAXN=1e5+10;
    int n,m,k,a[MAXM],b[MAXM],c[MAXN],f[35][MAXN];
    int main(){
    	read(n);read(m);read(k);
    	for(int i=1;i<=m;i++)read(a[i]),read(b[i]);
    	for(int i=1;i<=n;i++)c[i]=i;
    	for(int i=1;i<=m;i++)reverse(c+a[i],c+b[i]+1);
    	for(int i=1;i<=n;i++)f[0][i]=c[i];
    	for(int i=1;i<=30;i++)
    		for(int j=1;j<=n;j++)
    			f[i][j]=f[i-1][f[i-1][j]];
    	for(int i=1;i<=n;i++){
    		int x=i,m=k;
    		for(int j=30;j>=0;j--)
    			if(m>=(1ll<<j)){
    				m-=(1ll<<j);
    				x=f[j][x];
    			}
    		writen(x);
    	}
    	return 0;
    }
    

    后记

    感谢 @LightningUZ 帮我调了这道题的代码,帮我调出了一个小错误。

    如果题解有误,欢迎在下面评论或私信我,使得这篇题解更好。

  • 相关阅读:
    web site 和 web application的区别
    Windows Phone开发(10):常用控件(上)
    WPF绑定ListBox
    Cookies的实际存储位置
    parse_str() 函数把查询字符串解析到变量中。
    str_repeat() 函数把字符串重复指定的次数。
    搭建Git本地服务器
    windows下github 出现Permission denied (publickey).解决方法
    ReadSolve 规格严格
    Scp命令(转载) 规格严格
  • 原文地址:https://www.cnblogs.com/zhaohaikun/p/12817037.html
Copyright © 2020-2023  润新知