zoukankan      html  css  js  c++  java
  • LOJ#3212. 「CSP-S 2019」划分

    Link

    考场上初一的我用链表瞎模拟得到了 (4 ext{pts}) 的好成绩,时隔一年半,不看题解还是只会 (36 ext{pts}),自闭了……


    4pts

    输出样例。

    12pts

    暴力枚举分段点。

    36pts

    (f(i,j)) 表示当前新划分的区间是 ([i,j]),划分完 (1sim j) 的最小代价,那么有转移:

    [f(i,j)=min_{sumlimits_{t=k}^{i-1}a_tle sumlimits_{t=i}^ja_t}left{f(k,i-1)+left(sum_{t=i}^{j}a_t ight)^2 ight} ]

    时间复杂度 (mathcal O(n^3)),期望得分 (36 ext{pts})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define int long long
    const int N=410;
    int sum[N],a[N],f[N][N];
    signed main()
    {
    	freopen("partition.in","r",stdin);
    	freopen("partition.out","w",stdout);
    	int n,t;scanf("%lld%lld",&n,&t);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    	memset(f,0x3f,sizeof(f));
    	for(int i=1;i<=n;i++) f[1][i]=sum[i]*sum[i];
    	for(int i=2;i<=n;i++)
    	{
    		for(int j=i;j<=n;j++)
    		{
    			for(int k=1;k<i;k++)
    				if(sum[j]-sum[i-1]>=sum[i-1]-sum[k-1])
    					f[i][j]=min(f[i][j],f[k][i-1]);
    			f[i][j]+=(sum[j]-sum[i-1])*(sum[j]-sum[i-1]);
    		}
    	}
    	int ans=0x3f3f3f3f3f3f3f3fll;
    	for(int i=1;i<=n;i++)ans=min(ans,f[i][n]);
    	printf("%lld",ans);
    	return 0;
    }
    

    64pts

    一个显然的性质:最后一段的和越小越好。那么可以将状态转为一维,(f(i)) 表示最后一个划分结尾为 (i)(1sim i) 的最少代价,(l_i) 表示满足转移条件的最大的 (j),那么有转移:

    [f(i)=min_{0<jle i ext{ 且 }sumlimits_{t=l_j+1}^ja_tle sumlimits_{t=j+1}^i a_t}left{f(j)+left(sum_{t=j+1}^i a_t ight)^2 ight} ]

    时间复杂度 (mathcal O(n^2)),期望得分 (64 ext{pts})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define int long long
    const int N=5e5+10;
    int a[N],f[N],l[N],sum[N];
    signed main()
    {
    	freopen("partition.in","r",stdin);
    	freopen("partition.out","w",stdout);
    	int n,t;scanf("%lld%lld",&n,&t);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    	memset(f,0x3f,sizeof(f));
    	f[0]=0;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=0;j<i;j++)
    		{
    			if(sum[i]-sum[j]>=sum[j]-sum[l[j]])
    			{
    				if(f[j]+(sum[i]-sum[j])*(sum[i]-sum[j])<f[i])
    				{
    					f[i]=f[j]+(sum[i]-sum[j])*(sum[i]-sum[j]);
    					l[i]=j;
    				}
    			}
    		}
    	}
    //	for(int i=1;i<=n;i++)printf("%lld ",f[i]);puts("");
    	printf("%lld",f[n]);
    	return 0;
    }
    

    88pts

    (sumlimits_{t=l_j+1}^ja_tle sumlimits_{t=j+1}^i a_t) 写成前缀和的形式,得到:

    [s_j-s_{l_j}le s_i-s_j ]

    移项可得:

    [s_ige 2s_j-s_{l_j} ]

    现在左边只和 (i) 相关,右边只和 (j) 相关,而我们只需要一个满足这个条件的最大的 (j),所以可以单调队列维护 (2s_j-s_{l_j}),时间复杂度 (mathcal O(n)),期望得分 (88 ext{pts})

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define int long long
    const int N=5e5+10;
    int a[N],f[N],l[N],sum[N],que[N],h=0,t=0;
    signed main()
    {
    	freopen("partition.in","r",stdin);
    	freopen("partition.out","w",stdout);
    	int n,t;scanf("%lld%lld",&n,&t);
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i];
    	for(int i=1;i<=n;i++)
    	{
    		while(h<t&&sum[que[h+1]]*2-sum[l[que[h+1]]]<=sum[i])h++;
    		l[i]=que[h];f[i]=f[l[i]]+(sum[i]-sum[l[i]])*(sum[i]-sum[l[i]]);
    		while(h<t&&sum[que[t]]*2-sum[l[que[t]]]>=sum[i]*2-sum[l[i]])t--;
    		que[++t]=i;
    	}
    	printf("%lld",f[n]);
    	return 0;
    }
    

    100pts

    卡常。

    不懂为什么要出这个部分分。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int N=4e7+10;
    typedef long long ll;
    ll sum[N];
    int a[N],la[N],que[N],h=0,t=0;
    int b[N],l[N],r[N],p[N];
    
    void Write(__int128 n)
    {
    
        if(n<0) {putchar('-');n=-n;}
    
        if(n>9) Write(n/10);
    
        putchar(n%10+'0');
    
        return;
    
    }
    signed main()
    {    
    	freopen("partition.in","r",stdin);
    	freopen("partition.out","w",stdout);
    	int n,t;scanf("%d%d",&n,&t);
    	if(t==0)
    	{
    	    for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
    	    for(int i=1;i<=n;i++)
    	    {
    		    while(h<t&&sum[que[h+1]]*2-sum[la[que[h+1]]]<=sum[i])h++;
    		    la[i]=que[h];
    		    while(h<t&&sum[que[t]]*2-sum[la[que[t]]]>=sum[i]*2-sum[la[i]])t--;
    		    que[++t]=i;
    	    }
    	    __int128 ans=0;
    	    int pos=n;
    	    while(pos)
    	    {
    	        ans+=(__int128)(sum[pos]-sum[la[pos]])*(sum[pos]-sum[la[pos]]);
    	        pos=la[pos];
            }
            Write(ans);
    	}
    	else
        {
            int x,y,z,m;scanf("%d%d%d%d%d%d",&x,&y,&z,&b[1],&b[2],&m);
            for(int i=1;i<=m;i++) scanf("%d%d%d",&p[i],&l[i],&r[i]);
            int n=p[m];
            for(int i=3;i<=n;i++) b[i]=((ll)x*b[i-1]+(ll)y*b[i-2]+z)%(1ll<<30ll);
            for(int i=1;i<=m;i++)
                for(int j=p[i-1]+1;j<=p[i];j++)
                    a[j]=(b[j]%(r[i]-l[i]+1))+l[i],sum[j]=sum[j-1]+a[j];
            for(int i=1;i<=n;i++)
    	    {
    		    while(h<t&&sum[que[h+1]]*2-sum[la[que[h+1]]]<=sum[i])h++;
    		    la[i]=que[h];
    		    while(h<t&&sum[que[t]]*2-sum[la[que[t]]]>=sum[i]*2-sum[la[i]])t--;
    		    que[++t]=i;
    	    }
    	    __int128 ans=0;
    	    int pos=n;
    	    while(pos)
    	    {
    	        ans+=(__int128)(sum[pos]-sum[la[pos]])*(sum[pos]-sum[la[pos]]);
    	        pos=la[pos];
            }
            Write(ans);
        }
    	return 0;
    }
    
  • 相关阅读:
    java API 文档
    java容器
    java学习笔记 java.io.File类
    理解性能的奥秘——应用程序中慢,SSMS中快(1)——简介
    第六章——根据执行计划优化性能(3)——键值查找
    第六章——根据执行计划优化性能(2)——查找表/索引扫描
    第六章——根据执行计划优化性能(1)——理解哈希、合并、嵌套循环连接策略
    SQL Server 执行计划操作符详解(3)——计算标量(Compute Scalar)
    SQL Server 执行计划操作符详解(2)——串联(Concatenation )
    SQL Server 执行计划操作符详解(1)——断言(Assert)
  • 原文地址:https://www.cnblogs.com/juruo-zzt/p/14724867.html
Copyright © 2011-2022 走看看