Codeforces Round #162 (Div. 1) C - 264C Choosing Balls

http://codeforces.com/problemset/problem/264/C

题目意思:

  n个小球排成一列,每个小球有两个属性:颜色ci,权值vi

  然后有q次询问,每次给出两个整数a和b

  求一个权值最大的小球子序列:

    一个序列的权值这样计算:

    当一个小球前面有小球,且颜色相同时,权值为a*vi

    否则为b*vi

    序列的权值为所有小球权值之和。

  长度为0的序列权值视为0,所以本题答案至少为0

 

 

很容易写出状态转移方程dp[i]表示以小球i结束的序列的最大权值

dp[i] = max( max(dp[j] + (ci == cj?a:b)*vi) (0<=j<i), b*vi )

  1.b*vi 为长度为1的只包含i的序列

  2.max(dp[j] + (ci == cj?a:b)*vi) (0<=j<i) 为当有前缀时的最大值

当然,同样的,复杂度为n^2,无法承受。

观察方程,我们可以发现,其实dp[i] = max(颜色与ci不同的j中最大的dp[j]+b*v1, 颜色与ci相同的j中最大的dp[j]+a*v1, b*v1)

那我们何不直接以颜色为状态呢?dp[i]表示以颜色i结束的序列的最大值。

状态方程变为

dp[ci] = max(b*vi, max(dp[j],j!=ci) + b*vi, dp[ci] + a*vi, dp[ci])

复杂度还是n^2没有优化的地方吗?

状态转移费用主要耗费在求max(dp[j] | j!=ci)上,我们何不干脆记录当前dp[0..n]的最大值呢?不过万一最大值的i正好与当前的ci相同怎么办?那就是次大的!

所以记录最大的和次大dp[i]即可完美解决问题!

当ci与最大的dp[i]颜色相同时,返回次大值,否则返回最大值。同时,每次更新后,更新一下最大值和次大值。

转移费用马上下降到了1,问题解决!

注意刚开始把所有dp[i]置为负无穷大,而不是0

代码如下:

 1 #include<cstdio>

 2 #include<cstring>

 3 #include<iostream>

 4 #include<algorithm>

 5 using namespace std;

 6 typedef long long ll;

 7 const int N=100100;

 8 const ll inf=~0ull>>3;

 9 const ll finf=-inf;

10 int n,c[N],v[N];

11 ll a,b,dp[N];

12 struct poi{

13     ll mx1,mx2;

14     int mxc1,mxc2;

15     void cl(){

16         mxc1=mxc2=-1;

17     }

18     ll getmax(int c){

19         if(mxc1 == -1) return finf;

20         else if(c!=mxc1) return mx1;

21         else if(mxc2 == -1) return finf;

22         else return mx2;

23     }

24     void add(int c,ll mx){

25         if(mxc1==-1){

26             mxc1=c;

27             mx1=mx;

28         }

29         else if(c==mxc1){

30             if(mx>mx1) mx1=mx;

31         }

32         else if(mx>mx1){

33             mxc2=mxc1;

34             mx2=mx1;

35             mxc1=c;

36             mx1=mx;

37         }

38         else if(mxc2==-1 || (mx > mx2)){

39             mxc2=c;

40             mx2=mx;

41         }

42     }

43 };

44 void solve(){

45     for(int i=0;i<n;i++)dp[i]=finf;

46     dp[c[0]]=b*v[0];

47     poi pmx;

48     pmx.cl();

49     pmx.add(c[0],b*v[0]);

50     for(int i=1;i<n;i++){

51         ll tmp=pmx.getmax(c[i]) + b*v[i];

52         ll tmp2=dp[c[i]] + a*v[i];

53         tmp=max(tmp,tmp2);

54         tmp2=b*v[i];

55         tmp=max(tmp,tmp2);

56         if(tmp > dp[c[i]]){

57             dp[c[i]]=tmp;

58             pmx.add(c[i],tmp);

59         }

60     }

61     ll ans=0;

62     for(int i=0;i<n;i++)if(dp[i]>ans) ans=dp[i];

63     printf("%I64d\n",ans);

64 }

65 int main()

66 {

67     int m;

68     while (~scanf("%d%d",&n,&m))

69     {

70         for(int i=0;i<n;i++)scanf("%d",v+i);

71         for(int i=0;i<n;i++){

72             scanf("%d",c+i);

73             c[i]--;

74         }

75         while(m--){

76             scanf("%I64d%I64d",&a,&b);

77             solve();

78         }

79     }

80     return 0;

81 }

 

 

 

你可能感兴趣的:(codeforces)