• [HNOI2017] 礼物


    Description

    给定两个手环,可以将其中一个手环中所有装饰物的亮度增加一个相同的非负整数 c。并且由于这个手环是一个圆,可以以任意的角度旋转它。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 (1 sim n),其中 (n) 为每个手环的装饰物个数, 第 (1) 个手环的 (i) 号位置装饰物亮度为 (x_i),第 (2) 个手环的 (i) 号位置装饰物亮度为 (y_i),两个手环之间的差异值为 sum_{i=1}^{n} (x_i-y_i)^2$。求这个最小值。

    Solution

    根据

    [sum_{i=1}^n (x_i - y_i + c)^2 = sum_{i=1}^n x_i^2 + sum_{i=1}^n y_i^2 - 2 sum_{i=1}^n x_i y_i + 2c(sum_{i=1}^n x_i-sum_{i=1}^n y_i) + nc^2 ]

    可以发现 (c) 与交叉乘积项(旋转控制项)相互独立,同时由于答案的非负性,我们只需要分别最小化二者即可。

    考虑到 (|c| le m)(c) 只需要暴力枚举即可。

    对于交叉乘积项,我们通过卷积来计算出所有旋转对应的结果。具体地,将第一个环倍增并记做 (A[0..2n-1]),第二个环翻转并记做 (B[0..n-1]),则卷积序列 (C=A*B)(C[n-1+i]) 即表示旋转 (i) 位后的交叉乘积项结果。我们只需要取一个最大值即可。

    #include<bits/stdc++.h>
    #define N 262145
    #define pi acos(-1)
    using namespace std;
    
    namespace po {
    typedef complex<double> E;
    int n,m,L;
    int R[N];
    E a[N],b[N];
    
    void fft(E *a,int f){
    	for(int i=0;i<n;i++)if(i<R[i])swap(a[i],a[R[i]]);
    	for(int i=1;i<n;i<<=1){
    		E wn(cos(pi/i),f*sin(pi/i));
    		for(int p=i<<1,j=0;j<n;j+=p){
    			E w(1,0);
    			for(int k=0;k<i;k++,w*=wn){
    				E x=a[j+k],y=w*a[j+k+i];
    				a[j+k]=x+y;a[j+k+i]=x-y;
    			}
    		}
    	}
    }
    
    void mul(int _n,int *aa,int *bb,int *cc){
        n=_n;
        memset(a,0,sizeof a);
        memset(b,0,sizeof b);
        memset(R,0,sizeof R);
        L=0;
        m=n;
    	for(int i=0,x;i<=n;i++)a[i]=aa[i];
    	for(int i=0,x;i<=m;i++)b[i]=bb[i];
    	m*=2;
    	for(n=1;n<=m;n<<=1)L++;
    	for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    	fft(a,1);fft(b,1);
    	for(int i=0;i<=n;i++)a[i]=a[i]*b[i];
    	fft(a,-1);
    	memset(cc,0,sizeof cc);
    	for(int i=0;i<=m;i++) cc[i]=(int)(a[i].real()/n+0.5);
    }
    }
    
    using po::mul;
    
    int n,m,x[N],y[N],z[N];
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        cin>>n>>m;
    
        for(int i=0;i<n;i++) cin>>x[i];
        for(int i=0;i<n;i++) cin>>y[i];
    
        int ans=0;
        for(int i=0;i<n;i++) ans+=x[i]*x[i]+y[i]*y[i];
    
        int a=0;
        for(int i=0;i<n;i++) a+=x[i]-y[i];
    
        int tans=1e18;
        for(int i=-m;i<=m;i++) tans=min(tans, n*i*i+2*i*a);
        ans+=tans;
        
        for(int i=0;i<n;i++) x[n+i]=x[i];
        reverse(y,y+n);
    
        mul(n*2,x,y,z);
        ans-=2*(*max_element(z+n-1,z+2*n-1));
    
        cout<<ans<<endl;
        return 0;
    }
    
  • 相关阅读:
    Java对ArrayList进行排序
    Android app Splash页的替代方案
    算法
    Android动画 三种动画
    oracle连接-会话-进程
    spring使用Redis自定义前缀后缀名(去掉SimpleKey []+自定义)
    Redis(RedisTemplate)运算、算法(incr、decr、increment)
    Redis(RedisTemplate)使用hash哈希
    Redis(RedisTemplate)使用list链表
    Redis(RedisTemplate)使用string字符串
  • 原文地址:https://www.cnblogs.com/mollnn/p/13757865.html
Copyright © 2020-2023  润新知