SRM 585 DIV1

A

  树形dp是看起来比较靠谱的做法 , 但是转移的时候不全面就会出错 , 从贪心的角度出发 , 首先让第一量车走最长路,

  然后就会发现递归结构 , 得到递归式 f[i] = ( f[i-2] + f[i-3] + .. + f[1]) * 2 + 1;

  贪心的正确性 , 可以根据dp方程证出来 , 不过还是蛮显然的...

  

 1 #define maxn 1000100

 2 #define mod 1000000007

 3 #define INF (1ll<<60)

 4 llong dp[maxn][3];

 5 class TrafficCongestion {

 6 public:

 7     int theMinCars(int);

 8 };

 9 

10 

11 llong dfs(int h,int s)

12 {

13     int i,j;

14     llong resa,resb,resc;

15     dp[0][0] = 1;

16     dp[0][1] = 0;

17     dp[0][2] = 1;

18     for ( i=1 ; i<=h ; i++ )

19     for ( j=0 ; j<3 ; j++ )

20     {

21         resa = resb =resc = INF;

22         if (j==0)

23         {

24             resa = dp[i-1][1] + dp[i-1][0] + 1ll;    resa %= mod;

25             resb = dp[i-1][2] + dp[i-1][0];    resb %= mod;

26             resc = dp[i-1][2] + dp[i-1][1];    resc %= mod;

27         }

28         else if (j==1)

29         {

30             resa = dp[i-1][0] * 2LL % mod;

31             resb = dp[i-1][0] + dp[i-1][1];    resb %= mod;

32         }

33         else if (j==2)

34         {

35             resa = dp[i-1][2] + dp[i-1][0];    resa %= mod;

36             resb = dp[i-1][0]*2LL + 1LL;    resb %= mod;

37         }

38         resa = min(resa,resb);

39         resa = min(resa,resc);

40         dp[i][j] = resa;

41     }

42     return dp[h][s];

43 }

44 int TrafficCongestion::theMinCars(int h)

45 {

46     memset(dp,-1,sizeof(dp));

47     llong ans = dfs(h,0);

48     return ans;

49 }
View Code

 

B

  假设每个value只有一个 , 那么只要构造递增次数为K的序列就行了 ,  构造方法可以是:先取前k个作为k个递增序列的起点 ,

  剩下的就是n-k个元素分配给,k个集合求方案数 , 但是这样无法保证每个序列递增 ,不过还是给我们一个启示:按递增顺序考虑.

  定义dp[i][j] 为 前i个元素 , 已经构造了j个递增序列的方案数.

  dp[i+1][j] = dp[i][j] *  j + dp[i][j-1]

  拓展到每个value可能出现多个的情况时 ,转移要乘上组合数.

 1 using namespace std;

 2 #define maxn 1300

 3 typedef long long llong;

 4 const llong mod = 1000000007;

 5 class LISNumber {

 6 public:

 7     int count(vector <int>, int);

 8 };

 9 llong dp[40][maxn],c[maxn][maxn];

10 int n,sum[maxn];

11 int LISNumber::count(vector <int> card, int K)

12 {

13     int i,j,k;

14     n = card.size();

15     for ( i=0 ; i<n ; i++ ) sum[i] = i? sum[i-1]+card[i] : card[i];

16 

17     for ( i=1,c[0][0]=1 ; i<maxn ; i++ )

18     for ( j=0 ; j<=i ; j++ ) 

19     {

20         c[i][j] = j?c[i-1][j-1]+c[i-1][j] : 1;

21         c[i][j] %= mod;

22     }

23 

24     memset(dp,0,sizeof(dp));

25     dp[0][0] = 1;

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

27     for ( j=0 ; j<=sum[i] && j<=K ; j++ ) if (dp[i][j])

28     {

29 //        printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);

30         for ( k=0 ; k<=j && k<=card[i] ; k++ )

31         {

32             llong pos,add,x;

33             add = card[i]-k;

34             pos = ( (i?sum[i-1]:0) + 1 ) - j + k;

35             x = c[j][k] * c[pos+add-1][pos-1] %mod * dp[i][j] % mod;

36 //            printf("add to dp[%d][%d] ,k=%d: %lld\n",i+1,(int)(j+add),k,x);

37             dp[i+1][j+add] += x;

38             dp[i+1][j+add] %= mod;

39         }

40     }

41 //    for ( i=0 ; i<=n ; i++ )

42 //    for ( j=0 ; j<=K ; j++ ) if (dp[i][j]) printf("dp[%d][%d]=%lld\n",i,j,dp[i][j]);

43     return dp[n][K] % mod;

44 }
View Code

 

C

  (计算几何不会 , 看题解撸了好久..)

  判断点是否在三角形内部或边界:

    顺时针地考虑每条边e , 如果黑点在e下方 , 则在内部 , 否则在外部 , 用叉积判断 , 注意叉积为0的时候 ,是恰好在边界上,应判为在内部;

  统计方案数:

    为了不重复统计 , 3元组(a,b,c)应该为升序 .

    于是有了朴素的办法: 枚举三元组.

    然后考虑: 对于确定的a , b有一个取值范围来保证 e(a,b) 这条边合法 , c也有一个取值范围来保证 e(c,a) 这条边合法 , 

    用f(i)来表示对于点i , 能取的编号最大的点 , 很显然f(i)是递增的 , 然后根据单调性 , 利用"部分和"的技巧 , 可以o(n)统计方案数.

 1 #define maxn (58585*4+100)

 2 class EnclosingTriangle {

 3 public:

 4     long long getNumber(int, vector <int>, vector <int>);

 5 };

 6 

 7 struct node {

 8     llong x,y;

 9 };node e[maxn];

10 int t,f[maxn];

11 

12 llong xmult(llong x0,llong y0,llong x1,llong y1) {

13     return x0*y1 - x1*y0;

14 }

15 

16 llong sum , add[maxn] , addid[maxn] , front , tail , c;

17 

18 int check(int A,int B,vector<int>x,vector<int> y) {

19     node a = e[A];

20     node b = e[B%t];

21     for (int i=0 ; i<(int)x.size() ; i++ ) {

22         if (xmult(a.x-x[i],a.y-y[i],b.x-x[i],b.y-y[i])>0) return 0;

23     }

24     return 1;

25 }

26 

27 void sub(llong d) {

28     sum -= (tail-front) * d;

29 //    printf("sub: cnt=%lld cut:%lld sum:%lld\n",tail-front,(tail-front)*d,sum);

30     while (front<tail && add[front]-c<0) {

31         sum -= add[front]-c;

32 //        printf("cut:%lld  count:%lld otq:%lld\n",add[front]-c,add[front],addid[front]);

33         front++;

34     }

35 }

36 

37 void ins(int b) {

38     add[tail] = min(f[b]+1,t);

39     addid[tail] = b;

40     sum += add[tail++]-c;

41 }

42 

43 long long EnclosingTriangle::getNumber(int m, vector <int> x, vector <int> y) {

44     for (int i=0 ; i<4 ; i++ ) {

45         for (int j=0 ; j<m ; j++ ) {

46             llong ox[] = {0,j,m,m-j};

47             llong oy[] = {j,m,m-j,0};

48             e[t++] = (node){ox[i],oy[i]};

49         }

50     }

51     for (int i=0,j=1 ; i<t ; i++ ) {

52         while (check(i,j,x,y)) j++;

53         j--;

54         f[i] = j;

55 //        printf ("f[%d]=%d\n",i,j);

56     }

57     c = 1;

58     llong res = 0;

59     for (int a=0,b=1 ; a<t ; a++ ) {

60         llong d = 0;

61 

62         while (front<tail && addid[front]<=a) {

63             sum -= add[front]-c;

64 //            printf("front:%lld tail:%lld cut:%lld sum:%lld\n",front,tail,add[front]-c,sum);

65             front++;

66         }

67         while (f[c]<a+t && c+1<t) c++,d++;

68         if (f[c]<a+t) break;

69         sub(d);

70 //        printf("before add :sum:%lld c:%lld\n",sum,c);

71         while (b<=f[a]) {

72             if (min(f[b]+1,t)-c>=0) {

73                 ins(b);

74                 if (b==c) res--;

75             }

76             b++;

77         }

78         res += sum;

79 //        printf("after add: sum:%lld c:%lld b:%d res:%lld\n",sum,c,b,res);

80     }

81     return res;

82 }
View Code

 

  

你可能感兴趣的:(div)