• [AH2017/HNOI2017]礼物


    m很小100,一个O(nm)的复杂度

    两个手环增加非负整数亮度,等于其中一个增加一个整数亮度(可以为负)

    显而易见,最多增加[-m,m]个亮度

    考虑O(n)枚举对齐方式,O(m)枚举差距的亮度

    亮度增加的时候,维护平方和,只用维护之前的平方和,所有项的和即可

    但是每次旋转,初始的对齐位置会发生改变,所以暴力可以O(N^2)

    我们只关心初始的平方和

    拆开,其实就要求∑ai*bj

    由于旋转,所以b倍长,

    对应下标差一定,求乘积

    把a数组reverse,然后NTT快乐搞定

    开始没有注意两个环都可以增亮,,所以只枚举了[0,m],没想到有95pts、。。。

    代码:

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    #define int long long
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=100000+5;
    const int mod=998244353;
    const int G=3;
    const int GI=332748118;
    const int inf=0x3f3f3f3f;
    int n,m;
    ll qm(ll x,ll y){
        ll ret=1;
        while(y){
            if(y&1) ret=ret*x%mod;
            x=x*x%mod;
            y>>=1;
        }
        return ret;
    }
    int rev[8*N];
    ll a[8*N],b[8*N];
    void NTT(ll *f,int n,int c){
        for(reg i=0;i<n;++i){
            if(i<rev[i]) swap(f[i],f[rev[i]]);
        }
        for(reg p=2;p<=n;p<<=1){
            ll gen;
            int len=p/2;
            if(c==1) gen=qm(G,(mod-1)/p);
            else gen=qm(GI,(mod-1)/p);
            for(reg l=0;l<n;l+=p){
                ll buf=1;
                for(reg k=l;k<l+len;++k){
                    ll tmp=buf*f[k+len]%mod;
                    f[k+len]=(f[k]-tmp+mod)%mod;
                    f[k]=(f[k]+tmp)%mod;
                    buf=buf*gen%mod;
                }
            }
        }
    }
    void calc(ll *f,ll *g,int n){
        NTT(f,n,1),NTT(g,n,1);
        for(reg i=0;i<n;++i) f[i]=f[i]*g[i]%mod;
        NTT(f,n,-1);
        ll inv=qm(n,mod-2);
        for(reg i=0;i<n;++i) f[i]=f[i]*inv%mod;
    }
    int main(){
        rd(n);rd(m);
        ll s1=0,s2=0;
        ll f1=0,f2=0;
        for(reg i=0;i<n;++i) scanf("%lld",&a[i]),s1+=a[i],f1+=a[i]*a[i];
        for(reg j=0;j<n;++j) scanf("%lld",&b[j]),s2+=b[j],f2+=b[j]*b[j];
        reverse(a,a+n);
        for(reg j=n;j<=2*n-1;++j) b[j]=b[j-n];
        int lp;
        int len=0;
        for(lp=2*n+2*n,len=1;len<lp;len<<=1);
        for(reg i=0;i<len;++i){
            rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0);
        }
    //    for(reg i=0;i<len;++i) printf("%d ",a[i]);puts("");
    //    for(reg i=0;i<len;++i) printf("%d ",b[i]);
        
        calc(a,b,len);// ans is in array a
    //    cout<<" len "<<len<<endl;
    //    for(reg i=0;i<len;++i){
    //        cout<<a[i]<<" ";
    //    }cout<<endl;
        
        ll ans=inf;
        for(reg i=0;i<n;++i){
            ll su=0,sf=0;
            su=s1-s2;
            sf=f1+f2-2*a[i+n];
            ans=min(ans,sf);
            for(reg j=1;j<=m;++j){
                sf+=2*su+n;
                su+=n;
                ans=min(ans,sf);
            }
            su=s1-s2;
            sf=f1+f2-2*a[i+n];
            for(reg j=1;j<=m;++j){
                sf-=2*su-n;
                su-=n;
                ans=min(ans,sf);
            }
        }
        printf("%lld",ans);
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2019/1/10 16:53:09
    */
    View Code
  • 相关阅读:
    描述一下Spring Bean的生命周期
    BeanFactory和ApplicationContext有什么区别
    谈谈你对AOP的理解
    谈谈对IOC的理解
    线程池中线程复用原理
    线程池中阻塞队列的最用?为什么是先添加队列而不是先创建最大线程
    为什么使用线程池?解释下线程池参数
    去噪声论文阅读
    怎么使用有三AI完成系统性学习
    JavaCnn项目注解
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10251385.html
Copyright © 2020-2023  润新知