zoukankan      html  css  js  c++  java
  • 题解 [POI2012] Leveling Ground

    Decsription

    给定一个长度为 (n) 的数组,每次操作可以将一个区间的数增加 (a) 或减少 (b) ,或将一个区间的数增加 (b) 或减少 (a)。求使整个数组变为 (0) 的最小操作次数。若无解请输出 (-1)


    Solution

    一道很有意思的思维题。不看题解根本不会。

    看到区间加减一个数的操作,我们可以想到转化为差分数组上单点加减。

    我们看到 (a)(b) 不难联想到这样一个方程 (ax+by=c)

    是的,我们可以通过exgcd来解决这个问题。

    无解的情况很显然:当存在 (c ot mid gcd(a,b)) 时,方程无解。(裴蜀定理)

    我们便找到了一组通解。

    考虑最小化操作次数 (|x|+|y|),不难发现它只有一下几种可能:

    • (x) 为最小非负数,

    • (x) 为最大非正数

    • (y) 为最小非负数

    • (y) 为最大非正数

    我们又知道通解 (x=x_0+k imesfrac b {gcd(a,b)} y=y_0+k imes frac a {gcd(a,b)})

    所以不难找到这四种解。


    但我们要注意全局的合法性,我们在差分时选择了两个数,一个加,一个减。

    所以我们总的 (summathrm{sign(a)}=0),对于 (b) 也是同理。

    显然,我们刚才的贪心并不满足这个性质,所以我们采用反悔堆。

    注意到 (x)(y) 具有方程关系,所以只要 (x) 满足条件 (y) 也一定满足。

    我们找出 (x,y) 中操作总和 (>0) 的一类,考虑如何使它变为 (0)

    (满足了 (ax+by=c),故一正一负。)令 (mathrm{cnt_x}>mathrm{cnt_y})

    通过上述通解,我们可以尝试每次将 (x) 减少 (frac a {gcd(a,b)})(y) 增加 (frac b {gcd(a.b)})

    用大根堆维护代价最大的一组,每次贪心的选它。

    再吧反悔的 (now-last) 加入就好。

    (sum x=0) 时停止即可。


    Code:

    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    using namespace std;
    #define int long long 
    #define PII pair<int,int>
    const int N=100009;
    int n,a,b,Ans;
    int A[N],X[N],Y[N];
    priority_queue<PII> Q;
    int exgcd(int a,int b,int &x,int &y)
    {
    	if(!b){x=1,y=0;return a;}
    	int GCD=exgcd(b,a%b,x,y);
    	int z=x;x=y,y=z-a/b*y;
    	return GCD;
    }
    inline bool pd(int x,int y,int X,int Y)
    {
    	return abs(x)+abs(y)<abs(X)+abs(Y);
    }
    signed main()
    {
    	scanf("%lld%lld%lld",&n,&a,&b);
    	for(int i=1;i<=n;i++) scanf("%lld",&A[i]);
    	for(int i=n+1;i>=1;i--) A[i]=A[i]-A[i-1];++n;
    	int x,y,d=exgcd(a,b,x,y);a/=d,b/=d;//exgcd特解
    	for(int i=1,xx,yy;i<=n;i++)
    	{
    		if(A[i]%d!=0) return puts("-1"),0;
    		xx=X[i]=(x*A[i]/d%b+b)%b;
    		yy=Y[i]=(A[i]/d-xx*a)/b;
    		xx-=b,yy+=a;
    		if(pd(xx,yy,X[i],Y[i])) X[i]=xx,Y[i]=yy;
    		yy=(y*A[i]/d%a+a)%a;
    		xx=(A[i]/d-yy*b)/a;
    		if(pd(xx,yy,X[i],Y[i])) X[i]=xx,Y[i]=yy;
    		yy-=a,xx+=b;
    		if(pd(xx,yy,X[i],Y[i])) X[i]=xx,Y[i]=yy;
    	}
    	int rest=0;
    	for(int i=1;i<=n;i++) rest+=X[i];
    	rest/=b;
    	if(rest<0) rest=-rest,swap(a,b),swap(X,Y);
    	for(int i=1;i<=n;i++) Q.push( make_pair(-(abs(X[i]-b)+abs(Y[i]+a)-abs(X[i])-abs(Y[i])) ,i));
    	while(rest--)
    	{
    		int u=Q.top().second;Q.pop();
    		X[u]-=b,Y[u]+=a;
    		Q.push( make_pair(-(abs(X[u]-b)+abs(Y[u]+a)-abs(X[u])-abs(Y[u])) ,u));
    	}
    	for(int i=1;i<=n;i++) Ans+=abs(X[i])+abs(Y[i]);
    	printf("%lld",Ans>>1);
    	return 0;
    }
    
  • 相关阅读:
    php环境下所有的配置文件以及作用
    获取登陆用户的ip
    curl模拟post和get请求
    linux 下安装php curl扩展
    php常用面试知识点
    git使用步骤
    laravel框架基础知识点
    ci框架基础知识点
    ajax
    Mysql 中需不需要commit
  • 原文地址:https://www.cnblogs.com/Alansp/p/13778848.html
Copyright © 2011-2022 走看看