• 【洛谷3681】[CERC2016] Dancing Disks(归并)


    题目链接

    • 有一张 (6 imes 6) 的网格图,初始 (n) 个盘子堆在 ((1,1)),从下往上依次为 (a_{1sim n})
    • 一次操作可以把某个格子中最上方若干个盘子按顺序移到右方或下方格子中的最上方。
    • 要求构造一种方案将所有盘子移到 ((6,6)),且满足从下往上 (a_i) 递增。
    • (1le nle 4 imes10^4)

    归并思想

    考虑设 (f_{i,j}) 表示通过若干操作最多能在 ((i,j)) 保证多少个盘子有序。

    特殊地,(f_{1,1}=1)((1,2))((2,1)) 可以根据 ((1,1)) 最上面两个盘子的大小选择一起移动或分别移动实现有序,所以 (f_{1,2}=f_{2,1}=2)

    而对于一般的格子 ((x,y)),它可以从所有格子 ((i,j) (1le ile x,1le jle y)) 归并得到,即 (f_{x,y}=sum_{1le ile x,1le jle y}f_{i,j})

    然后发现这样求出的 (f_{6,6}=42960),能够满足条件。

    递归的具体实现

    具体实现就考虑一个递归的过程。注意,若想让当前盘子自下往上递增,需要让先前盘子自下往上递减。

    按自下往上、自右往左的顺序搞可能会方便一些,因为右下方的格子在求解的过程中会用到左上方的格子。

    每次就是把当前格子需要的份额分给先前的格子。当然,肯定不能超过每个格子对应的 (f_{i,j})

    然后用一个堆维护归并的过程,但数据范围这么小直接暴力似乎也行。

    代码:(O(36nlog36))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 40000
    using namespace std;
    int n,tp,a[N+5],f[7][7],g[7][7];vector<int> p[7][7];
    I void M(CI x1,CI y1,CI x2,CI y2) {for(RI i=x1;i^x2;++i) printf("%d %d D 1
    ",i,y1);for(RI i=y1;i^y2;++i) printf("%d %d R 1
    ",x2,i);}
    priority_queue<pair<int,int> > q[7][7];I void Solve(CI x,CI y,RI sz,CI op)//递归
    {
    	if(x==1&&y==1) return p[x][y].push_back(a[tp--]);//特判(1,1)
    	if(x==1&&y==2)//特判(1,2)
    	{
    		if(sz==1) return puts("1 1 R 1"),p[x][y].push_back(a[tp--]);//只需一个
    		if(op*a[tp-1]>op*a[tp]) return puts("1 1 R 2"),tp-=2,p[x][y].push_back(a[tp+1]),p[x][y].push_back(a[tp+2]);//一起移动
    		return puts("1 1 R 1"),puts("1 1 R 1"),p[x][y].push_back(a[tp--]),p[x][y].push_back(a[tp--]);//分别移动
    	}
    	if(x==2&&y==1)//特判(2,1)
    	{
    		if(sz==1) return puts("1 1 D 1"),p[x][y].push_back(a[tp--]);//只需一个
    		if(op*a[tp-1]>op*a[tp]) return puts("1 1 D 2"),tp-=2,p[x][y].push_back(a[tp+1]),p[x][y].push_back(a[tp+2]);//一起移动
    		return puts("1 1 D 1"),puts("1 1 D 1"),p[x][y].push_back(a[tp--]),p[x][y].push_back(a[tp--]);//分别移动
    	}
    	#define PUSH(i,j) q[x][y].push(make_pair(op*p[i][j].back(),i*6+j-1))//将(i,j)最上方的盘子入堆
    	RI i,j;for(i=x;i;--i) for(j=y;j;--j) if(i^x||j^y) if(Solve(i,j,min(sz,f[i][j]),-op),PUSH(i,j),(sz-=f[i][j])<=0) goto Calc;//将需要份额分给先前格子,注意反序
    	Calc:pair<int,int> k;W(!q[x][y].empty()) k=q[x][y].top(),q[x][y].pop(),//取出堆顶
    		p[x][y].push_back(k.first/op),i=k.second/6,j=k.second%6+1,M(i,j,x,y),p[i][j].pop_back(),!p[i][j].empty()&&(PUSH(i,j),0);//放入对应位置新的最上方盘子
    }
    int main()
    {
    	RI i,j;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);
    	for(i=1;i<=6;++i) for(j=1;j<=6;++j)
    	{
    		if(i==1&&j==1) {f[i][j]=g[i][j]=1;continue;}if(i==1&&j==2||i==2&&j==1) {f[i][j]=2,g[i][j]=3;continue;}//特判(1,1),(1,2),(2,1)
    		f[i][j]=g[i-1][j]+g[i][j-1]-g[i-1][j-1],g[i][j]=f[i][j]<<1;//一般情况
    	}return tp=n,Solve(6,6,n,1),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    修改默认runlevel
    shell数学运算
    Ubuntu碎碎念
    编译Linux-2.6.23内核中遇见的错误
    CentOS设置静态IP
    多线程--对象锁和类锁
    [Unity移动端]Touch类
    [Lua]string与中文
    MQTT 5.0 新特性(四)Clean Start 与 Session Expiry Interval
    EMQ 9 月 新发 | EMQ X Enterprise 3.4.0 功能概览
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3681.html
Copyright © 2020-2023  润新知