把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

CF958C3 Encryption (hard)

题面传送门
首先发现如果按照C2那个做法我们可以暴力\(O(nkp)\)dp
考虑这个膜有啥性质,不难想到如果一个段两边前缀膜是一样的那么这个段的答案就是\(0\)
\(n>kp\)的时候由抽屉原理必有\(k\)个相同的前缀膜,也就是说答案不超过\(\sum\limits_{i=1}^{n}{A_i}\mod p+p\)
又可以发现答案只有两种,只要判断是不是最小的就好了。
也就是说要每个段的端点前缀膜不降,跑个LIS就好了。
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (500000+5)
#define M (100+5)
#define K (1000+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar() 
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) (n*(x-1)+(y))
#define R(n) (rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,k,p,A[N],Q[N];
namespace Solve1{
	int dp[M][M*M],Fl[M];
	I void calc(){
		int i,j,h;Me(dp,0x3f);dp[0][0]=0;
		for(i=1;i<=k;i++){
			Me(Fl,0x3f);for(j=0;j<=n;j++){for(h=0;h<p;h++) dp[i][j]=min(dp[i][j],Fl[h]+(Q[j]-h+p)%p);Fl[Q[j]]=min(Fl[Q[j]],dp[i-1][j]);}
		}printf("%d\n",dp[k][n]);
	}
}
namespace Solve2{
	int dp[N],Fl[M];
	I void calc(){
		int i,j,h;Me(dp,-0x3f);dp[0]=0;Fl[0]=0;for(i=1;i<=n;i++){
			for(j=0;j<=Q[i];j++) dp[i]=max(dp[i],Fl[j]+1);Fl[Q[i]]=max(Fl[Q[i]],dp[i]); 
		}printf("%d\n",dp[n]>=k?Q[n]:Q[n]+p);
	}
}
int main(){
	freopen("1.in","r",stdin);
	int i;scanf("%d%d%d",&n,&k,&p);for(i=1;i<=n;i++) scanf("%d",&A[i]);for(i=1;i<=n;i++) Q[i]=(Q[i-1]+A[i])%p;
	if(n<=k*p) Solve1::calc();else Solve2::calc();
}
posted @ 2022-04-20 18:42  275307894a  阅读(29)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end