hdu 4455 dp

题意:给定一个序列ai,个数为n。再给出一系列w;对于每个w,求序列中,所有长度为w的连续子串中的权值和,子串权值为子串中不同数的个数

 

去了几个头文件,然后终于A了

 

以样例为例说明:
1 1 2 3 4 4 5;
明显dp[1]=n=7;
长度为1的时候有7个区间。从长度为1到长度为2,就是把前6个区间往后增加一个数,把最后一个区间去掉。
增加的6个数要看在该区间是否出现过,只要看它上一个相等的元素距离是否大于2
所以dp[2]=dp[1]-1+4;
以此类推就可以得出所以的dp值了。
dp[i]=dp[i-1]-A+B;
减的A是最后一个长度为i-1的区间的不同数的个数,这个很容易预处理得出来。
加的B是第t个数到它上一个数的距离大于i-1的个数.
这个B值也容易得出。
用s[i]表示离上一个数的距离为i的个数,不断减掉就得到B了。
 1 #include<cstdio>

 2 #include<cstring>

 3 using namespace std;

 4 #define ts printf("*****\n");

 5 #define cl(a) memset(a,0,sizeof(a))

 6 const int maxn=1000007;

 7 long long dp[maxn];

 8 int dis[maxn]; //第i个数和前面最近相同的数的距离 例如1,2,1    d[3]=2

 9 int r[maxn]; //和前面最近相同的数的距离为i的个数

10 int vis[maxn];

11 int lastp[maxn]; //数i的最近上一个位置

12 int a[maxn];

13 int dif[maxn]; //从n-i+1到n不同的数的个数

14 

15 int main()

16 {

17     int i,j,k;

18     #ifndef ONLINE_JUDGE

19     freopen("1.in","r",stdin);

20     #endif

21     int n;

22     while(scanf("%d",&n)!=EOF)

23     {

24         if(n==0)    break;

25         cl(lastp);

26         for(i=1;i<=n;i++)

27         {

28             scanf("%d",a+i);

29             dis[i]=i-lastp[a[i]];

30             lastp[a[i]]=i;

31         }

32         cl(r);

33         for(i=1;i<=n;i++)

34         {

35             r[dis[i]]++;

36         }

37         cl(dif);

38         cl(vis);

39         int t=0;

40         for(i=n;i>0;i--)

41         {

42             if(!vis[a[i]])

43             {

44                 vis[a[i]]=1;

45                 t++;

46             }

47             dif[n-i+1]=t;

48         }

49         int sum=n;

50         cl(dp);

51         dp[1]=n;

52         for(i=2;i<=n;i++)

53         {

54             dp[i]+=dp[i-1]-dif[i-1];//减掉最后一个区间的种类数

55             sum-=r[i-1];

56             dp[i]+=sum;

57         }

58         int q,tt;

59         scanf("%d",&q);

60         while(q--)

61         {

62             scanf("%d",&tt);

63             printf("%I64d\n",dp[tt]);

64         }

65     }

66     return 0;

67 }

 

你可能感兴趣的:(HDU)