Codeforces958C3 Encryption (hard)

Problem

Codeforces

Solution

是一道很有意思的题目。

首先你可以写出一个最暴力的方程,设 f [ i ] [ j ] f[i][j] f[i][j]表示前j个位置分i段的最小代价
f [ i ] [ j ] = min ⁡ ( f [ i − 1 ] [ k ] + ( s u m [ j ] − s u m [ k ] )   m o d   p ) f[i][j]=\min (f[i-1][k]+(sum[j]-sum[k])\bmod p) f[i][j]=min(f[i1][k]+(sum[j]sum[k])modp)

考虑优化,注意到模数很小,不妨按照前缀和模p的余数进行分组

g [ i ] [ j ] = min ⁡ r ≤ n o w , s u m [ r ] ≡ j ( f [ i ] [ r ] ) g[i][j]=\min_{r\leq now,sum[r]\equiv j}(f[i][r]) g[i][j]=minrnow,sum[r]j(f[i][r])

则可以做到 O ( n k p ) : O(nkp): O(nkp): f [ i ] [ j ] = min ⁡ r = 0 p ( g [ i − 1 ] [ r ] + ( s u m [ j ] − r )   m o d   p ) f[i][j]=\min_{r=0}^p (g[i-1][r]+(sum[j]-r)\bmod p) f[i][j]=minr=0p(g[i1][r]+(sum[j]r)modp),套路地分类讨论一下即可用树状数组维护然后得到一个 O ( n k log ⁡ p ) O(nk\log p) O(nklogp)的优秀算法。

∀ r ≤ s u m [ j ] f [ i ] [ j ] = min ⁡ ( g [ i − 1 ] [ r ] − r + s u m [ j ] ) \forall_{r\leq sum[j]}f[i][j]=\min(g[i-1][r]-r+sum[j]) rsum[j]f[i][j]=min(g[i1][r]r+sum[j])

∀ r > s u m [ j ] f [ i ] [ j ] = min ⁡ ( g [ i − 1 ] [ r ] − r + s u m [ j ] + p ) \forall_{r>sum[j]}f[i][j]=\min(g[i-1][r]-r+sum[j]+p) r>sum[j]f[i][j]=min(g[i1][r]r+sum[j]+p)

然后接下来怎么办呢。。首先答案肯定是同余与sum[n]的,考虑一下我们什么时候能得到最优解,假设sum表示前缀和模p的值,那么如果存在一个以sum[n]结尾的不降子序列长度大于等于k,那么就可以使得代价最小为sum[n],因为我们只需要分别以这些sum结尾来分段即可。

但是这还是可能被卡。那么再来讨论一下,如果存在k-1个sum值相同,那么就可以构造分别以这些sum值和n结尾来分段,这样就只有第一段和第k段做贡献了,而由于有了第一个判定,答案不可能为sum[n],而两端的贡献又不可能大于2p,所以答案就是sum[n]+p啦。根据抽屉原理,我们可以知道当 n > p ∗ ( k − 2 ) n> p*(k-2) n>p(k2)时必定存在这样的情况,那么就都可以被判定掉了。如此以来n就缩小到了可做的范围内。

Code

#include <algorithm>
#include <cstring>
#include <cstdio>
#define rg register
#define lowbit(x) ((x)&(-(x)))
using namespace std;
typedef long long ll;
const int maxn=210,INF=0x3f3f3f3f;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
int n,k,mod,a[1000010],sum[1000010],rk[maxn],f[maxn][40010];
struct BIT{
	int a[maxn];
	BIT(){memset(a,0x3f,sizeof(a));}
	void update(int p,int v){++p;for(;p<=mod;p+=lowbit(p)) getmin(a[p],v);}
	int query(int p){int res=INF;++p;for(;p>0;p-=lowbit(p)) getmin(res,a[p]);return res;}
}L[maxn],R[maxn];
inline int pls(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int lis()
{
	memset(rk,0x3f,sizeof(rk));
	for(rg int i=1;i<=n;i++) *upper_bound(rk+1,rk+k+1,sum[i])=sum[i];
	return rk[k]<=sum[n];
}
void dp()
{
	memset(f,0x3f,sizeof(f));
	L[0].update(0,0);
	for(rg int j=1;j<=n;j++)
	  for(rg int i=k;i;i--)
	  {
		getmin(f[i][j],L[i-1].query(sum[j])+sum[j]);
		getmin(f[i][j],R[i-1].query(mod-sum[j])+sum[j]+mod);
		L[i].update(sum[j],f[i][j]-sum[j]);
		R[i].update(mod-sum[j],f[i][j]-sum[j]);
	  }
	printf("%d\n",f[k][n]);
}
int main()
{
	read(n);read(k);read(mod);
	if(k>n){printf("%d\n",INF);return 0;}
	for(rg int i=1;i<=n;i++){read(a[i]);sum[i]=(sum[i-1]+a[i])%mod;}
	if(lis()) printf("%d\n",sum[n]);
	else if(n>k*mod) printf("%d\n",sum[n]+mod);
	else dp();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值