A
树形dp是看起来比较靠谱的做法 , 但是转移的时候不全面就会出错 , 从贪心的角度出发 , 首先让第一量车走最长路,
然后就会发现递归结构 , 得到递归式 f[i] = ( f[i-2] + f[i-3] + .. + f[1]) * 2 + 1;
贪心的正确性 , 可以根据dp方程证出来 , 不过还是蛮显然的...
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
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可能出现多个的情况时 ,转移要乘上组合数.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }
C
(计算几何不会 , 看题解撸了好久..)
判断点是否在三角形内部或边界:
顺时针地考虑每条边e , 如果黑点在e下方 , 则在内部 , 否则在外部 , 用叉积判断 , 注意叉积为0的时候 ,是恰好在边界上,应判为在内部;
统计方案数:
为了不重复统计 , 3元组(a,b,c)应该为升序 .
于是有了朴素的办法: 枚举三元组.
然后考虑: 对于确定的a , b有一个取值范围来保证 e(a,b) 这条边合法 , c也有一个取值范围来保证 e(c,a) 这条边合法 ,
用f(i)来表示对于点i , 能取的编号最大的点 , 很显然f(i)是递增的 , 然后根据单调性 , 利用"部分和"的技巧 , 可以o(n)统计方案数.
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }