[SRM] 09 撕书狂魔CZL

A. 撕书Ⅰ

序列型DP。DP[i]表示当前编号结点的撕书页数。

那么我们有 DP[ i ] = DP[ i - y - 1 ] + y

其中y为编号i书页对应范围内的书页。

那么,具体实现的话,需要求出每个i对应的y,这里用前缀和。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #define maxn 1000005
 5 #define lowbit(x) (-x&x)
 6 using namespace std;
 7 
 8 int n,high = 0;
 9 
10 struct node{
11     int pos,ai;
12 }list[maxn];
13 
14 int Tree[maxn],DP[maxn],minn;
15 int sum(int p){
16     int ans = 0;
17     while(p){
18 //        cout << '^';
19 //        cout << p;
20         ans += Tree[p];
21         p -= lowbit(p);
22     }
23     return ans;
24 }
25 void add(int p){
26     while(p <= high){
27 //        cout << 'G';
28         Tree[p]++;
29         p += lowbit(p);
30 //        cout << p;
31     }
32 }
33 
34 bool cmp(const node &a,const node &b){
35     return a.pos < b.pos;
36 }
37 
38 int main(){
39     scanf("%d",&n);
40     
41     for(int i = 1;i <= n;i++){
42         scanf("%d%d",&list[i].pos,&list[i].ai);
43         list[i].pos++;
44         high = max(high,list[i].pos);
45     }
46     
47     sort(list+1,list+1+n,cmp);
48     
49 //    printf("#%d#\n",high);
50     
51     for(int i = 1;i <= n;i++){
52         
53 //        cout << 'A';
54         add(list[i].pos);
55     }
56     
57     for(int i = 1;i <= n;i++){
58         int a = list[i].pos-1;
59         int b = list[i].pos-list[i].ai-1;
60         if(a < 0) a = 0;
61         if(b < 0) b = 0;
62         int y = sum(a)-sum(b);
63         if(i-y-1 < 0) continue;
64         DP[i] = DP[i-y-1] + y;
65     }
66     
67     minn = DP[n];
68     
69     for(int i = 1;i <= n;i++){
70         minn = min(minn,DP[i]+n-i);
71     }
72     
73     printf("%d",minn);
74     
75 //    cout << endl;
76 //    for(int i = 0;i <= n;i++) printf("%d ",DP[i]);
77     
78 //    cout << endl;
79 //    for(int i = 0;i <= 10;i++) printf("%d ",sum(i));
80     
81     return 0;
82 }
震惊!CYC的背景居然隐藏着这样的...

 

B. 撕书 Ⅱ

这道题略坑。

x表示a+b,y表示a^b,那么我们定义一个z = (x-y)>> 1。

这个z表示的就是有a+b进位的位(准确来说是a x b == 1)

为什么呢?

因为某些原因,异或等于没有进位的加,那么和就拥有异或结果所没有的进位的信息。

和 - 异或 就可以去除其他冗余,得到进位的信息啦!

然而需要右移一位才能精确表示。

那么显然不合法的情况就包括:

  1. x < y :显然根据前文我们有 x ≥ y

  2. ( x - y ) & 1 != 0: ( x - y )>>1 表示有进位的二进制位,如果命题成立,-1位有进位是什么鬼??

还有特殊情况:

  如果 x == y ,ans 需要减少2

  因为此时 a 和 b 可以有一个为0,所以需要排除这种情况。

求解策略:

  如果 y 的一个二进制位为 1 ,显然 a 和 b 其中一个为 0 且另一个为 1 ,那么有两种情况,ans *= 2

  如果 y 的一个二进制位为 0 ,显然 a 和 b 的对应二进制位是相同的,ans *= 1

以此检验 x 和 y 的每一位,ans也就出来了。

 1 #include<cstdio>
 2 #include<iostream>
 3 #define LL long long
 4 using namespace std;
 5 
 6 LL x,y,z,ans = 1;
 7 
 8 int main(){
 9     scanf("%lld%lld",&x,&y);
10     
11     z = x-y;
12     
13     if(z&1L || x < y){
14         printf("0");
15         return 0;
16     }
17     
18     z >>= 1;
19     
20 //    for(LL i = 1;i <= x;i<<=1){
21 //        printf("%d",(z&i)?1:0);.
22 //    }
23     
24 //    cout << endl;
25     
26     for(LL a = 0;(1L<<a) <= x;a++){
27 //        printf("%d",((1L<<a)&x)^((1L<<a)&z)?1:0);
28         if(y&(1L<<a)){
29             if(z&(1L<<a)){
30 //                printf("*%lld*\n",a);
31 //                if((x&a) == (z&a)) cout << " dfdfdfd";
32 //                cout << "#" << (x&a);
33 //                cout << "#" << (z&a);
34                 printf("0");
35                 return 0;
36             }
37             
38             ans *= 2;
39         }
40 //        if(y&a) ans *= 2;
41     }
42     
43     if(x == y) ans -= 2;
44     
45     
46     printf("%lld",ans);
47     
48     return 0;
49 }
题解比代码重要!

 

 

posted @ 2017-08-07 00:42  μSsia  阅读(228)  评论(0编辑  收藏  举报