HDU_5247(15百度之星B题)
做法:dp[i] = dp[i-1] + (i-k+1...i这段符合+1,否则+0)。如何判断这一段符不符合,找出这段的最大值,最小值,如何差值==k-1,并且里面的数没有重复,那么就可以+1,否则,就不能+1。最大值最小值用RMQ预处理一下是nlogn的复杂度,然后查找操作,因为长度一定所以就是o(1),问题的关键就是如何判断这个区间里面是否有重复的数,(n*k)的复杂度水过去的。。。,rep[i][j]表示i-j+1...i这个串是否有重复的,那么rep[i][j] = rep[i-1][j-1] || rep[i][j-1] || (num[i] == num[i-j+1]) 。然后就直接写了,然而我比赛搞了一半去装电脑去了。。。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #define FOR(i,x,y) for(int i = x;i < y;i ++) #define IFOR(i,x,y) for(int i = x;i > y;i --) using namespace std; int n,m,k,k_tw; int num[11111]; int maxx[11111][15],minx[11111][15]; int dp[11111]; bool rep[11111][1111]; void INIT_RMQ(int bit){ FOR(i,0,n){ maxx[i][0] = minx[i][0] = num[i]; } FOR(j,1,bit+1){ IFOR(i,n-1,-1){ if((i+(1<<(j-1))) < n){ maxx[i][j] = max(maxx[i][j-1],maxx[i+(1<<(j-1))][j-1]); minx[i][j] = min(minx[i][j-1],minx[i+(1<<(j-1))][j-1]); } } } FOR(i,0,n) rep[i][1] = 0; FOR(j,2,1002){ FOR(i,j-1,n){ if(rep[i-1][j-1] || rep[i][j-1] || num[i] == num[i-j+1]){ rep[i][j] = 1; continue; } rep[i][j] = 0; } } } void solve(){ if(rep[k-1][k] || max(maxx[0][k_tw],maxx[k-(1<<k_tw)][k_tw])-min(minx[0][k_tw],minx[k-(1<<k_tw)][k_tw]) != k-1) dp[k-1] = 0; else dp[k-1] = 1; FOR(i,k,n){ dp[i] = dp[i-1]; if(!rep[i][k] && max(maxx[i-k+1][k_tw],maxx[i+1-(1<<k_tw)][k_tw])-min(minx[i-k+1][k_tw],minx[i+1-(1<<k_tw)][k_tw]) == k-1){ dp[i]++; } } } int main() { //freopen("test.in","r",stdin); int tCase = 0; while(~scanf("%d%d",&n,&m)){ printf("Case #%d:\n",++tCase); FOR(i,0,n) scanf("%d",&num[i]); int tem = 0; while((1<<tem) < n){ tem++; } tem--; INIT_RMQ(tem); FOR(i,0,m){ scanf("%d",&k); k_tw = 0; while((1 << k_tw) <= k){ k_tw ++; } k_tw--; solve(); printf("%d\n",dp[n-1]); } } return 0; }