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

 

  

转载于:https://www.cnblogs.com/eggeek/p/3592629.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值