1001,官方题解是直接dp,首先dp[i]表示到i位置的种类数,它首先应该等于dp[i-1],(假设m是B串的长度)同时,如果(i-m+1)这个位置开始到i这个位置的这一串是和B串相同的,那么dp[i]还应该加上dp[i-m],因为从i-m+1开始可以被替换成另外一种意思。详细的见代码吧。我们当时使用dfs来做的,实际上换汤不换药,思想是一样的(不过dfs的话是从前往后算的)。代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 100000 + 5;
 4 const int mod = (int)1e9 + 7;
 5 
 6 char a[N],b[N];
 7 int nxt[N],n,m,dp[N];
 8 bool isok[N];
 9 
10 void getnxt()
11 {
12     memset(nxt,0,sizeof(nxt));
13     nxt[1] = 0;
14     int j = 0;
15     for(int i=2;b[i];i++)
16     {
17         while(j>0 && b[j+1]!=b[i]) j = nxt[j];
18         if(b[j+1] == b[i]) j++;
19         nxt[i] = j;
20     }
21 }
22 
23 void kmp()
24 {
25     memset(isok,false,sizeof(isok));
26     int j = 0;
27     for(int i=1;a[i];i++)
28     {
29         while(j>0 && b[j+1]!=a[i]) j=nxt[j];
30         if(b[j+1]==a[i]) j++;
31         if(j==m)
32         {
33             isok[i-m+1] = true;
34             j = nxt[j];
35         }
36     }
37 }
38 
39 int dfs(int x)
40 {
41     if(x==n+1) return 1;
42     if(dp[x] != -1) return dp[x];
43     if(!isok[x]) return dp[x] = dfs(x+1)%mod;
44     else
45     {
46         int ret = 0;
47         ret = dfs(x+1) % mod;
48         ret += dfs(x+m) % mod;
49         return dp[x] = ret%mod;
50     }
51 }
52 
53 int main()
54 {
55     int T;scanf("%d",&T);
56     for(int kase=1;kase<=T;kase++)
57     {
58         scanf("%s%s",a+1,b+1);
59         m=strlen(b+1);n=strlen(a+1);getnxt();kmp();
60 
61         /*memset(dp,0,sizeof(dp));
62         dp[0] = 1;
63         for(int i=1;i<=n;i++)
64         {
65             dp[i] = dp[i-1];
66             if(i-m+1>=1)
67             {
68                 if(isok[i-m+1]) dp[i] += dp[i-m];
69             }
70             dp[i] %= mod;
71         }
72         printf("Case #%d: %d\n",kase,dp[n]);*/
73         // 上面是题解的方法
74         // 下面是队友的方法
75         memset(dp,-1,sizeof(dp));
76         int ans = dfs(0) % mod;
77         printf("Case #%d: %d\n",kase,ans);
78     }
79 }
View Code

 

  1010,LIS,题解的方法很不错,数列中的每个数都减去这个数字之前的0的个数再做LIS,然后最后最大的LIS数加上整个串内0的个数即可。代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 100000 + 5;
 4 const int inf = 0x3f3f3f3f;
 5 
 6 int a[N],dp[N];
 7 
 8 int main()
 9 {
10     int T;scanf("%d",&T);
11     for(int kase=1;kase<=T;kase++)
12     {
13         int n;scanf("%d",&n);
14         for(int i=1;i<=n;i++) scanf("%d",a+i);
15         memset(dp,inf,sizeof(dp));
16         int cnt = 0;
17         for(int i=1;i<=n;i++)
18         {
19             if(a[i])
20             {
21                 a[i] -= cnt;
22                 *lower_bound(dp+1,dp+1+n,a[i]) = a[i];
23             }
24             else cnt++;
25         }
26         int ans = lower_bound(dp+1,dp+1+n,inf) - (dp+1) + cnt;
27         printf("Case #%d: %d\n",kase,ans);
28     }
29 }
View Code

另外,队友当时是用dp加二分的方法做的,但是我看不懂0.0,代码先放下,以后再看吧= =:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1e5+10;
 4 int num[N];
 5 int dp[N];
 6 
 7 int erfen(int l,int r,int x)
 8 {
 9     if(r == l) return r;
10     if(x > dp[r]) return r;
11     while(r - l > 1)
12     {
13         int mid = (r+l) /2;
14         if(dp[mid] <= x) l = mid;
15         else r = mid;
16     }
17     return r;
18 }
19 
20 
21 int main()
22 {
23     int T;
24     cin >> T;
25     for(int cnt = 1;cnt <= T;cnt ++)
26     {
27         int n;
28         scanf("%d",&n);
29         for(int i= 1;i <= n;i ++)
30         {
31             scanf("%d",&num[i]);
32         }
33         memset(dp,0x3f,sizeof(dp));
34         int top = 1;
35         for(int i = 1;i <= n;i ++)
36         {
37             if(num[i]!=0)
38             {
39                 int now = erfen(0,top,num[i]);
40                 if(num[i] == dp[now-1]) continue;
41                 dp[now] = num[i];
42                 if(now == top) top ++;
43             }
44             else
45             {
46 
47                 int now = erfen(0,top,num[i]);
48                 //cout << i << ' ' << now << ' ' << top << endl;
49 
50                 for(int j = top-1;j >= now ;j --)
51                 {
52                     if(dp[j] + 1 < dp[j+1]) dp[j+1] = dp[j]+1;
53                 }
54                 top ++;
55                 if(now == 1||dp[now-1] == 0) {dp[now] = 0;}
56 
57 
58             }
59             /*for(int i= 1;i < top;i ++) cout << i << ' ' << dp[i] << endl;
60             cout <<"##########" << i << ' ' <<top << endl;*/
61         }
62         printf("Case #%d: %d\n",cnt,top-1);
63     }
64 
65     return 0;
66 }
View Code

 

  1011,是个大水题,反正我当时题目也没看= =。现在看了一下队友的代码,getline的方法还是值得一学的。队友代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 string s[71] = {
 4 "Cleveland Cavaliers",
 5 "Golden State Warriors",
 6 "San Antonio Spurs",
 7 "Miami Heat",
 8 "Miami Heat",
 9 "Dallas Mavericks",
10 "L.A. Lakers",
11 "L.A. Lakers",
12 "Boston Celtics",
13 "San Antonio Spurs",
14 "Miami Heat",
15 "San Antonio Spurs",
16 "Detroit Pistons",
17 "San Antonio Spurs",
18 "L.A. Lakers",
19 "L.A. Lakers",
20 "L.A. Lakers",
21 "San Antonio Spurs",
22 "Chicago Bulls",
23 "Chicago Bulls",
24 "Chicago Bulls",
25 "Houston Rockets",
26 "Houston Rockets",
27 "Chicago Bulls",
28 "Chicago Bulls",
29 "Chicago Bulls",
30 "Detroit Pistons",
31 "Detroit Pistons",
32 "L.A. Lakers",
33 "L.A. Lakers",
34 "Boston Celtics",
35 "L.A. Lakers",
36 "Boston Celtics",
37 "Philadelphia 76ers",
38 "L.A. Lakers",
39 "Boston Celtics",
40 "L.A. Lakers",
41 "Seattle Sonics",
42 "Washington Bullets",
43 "Portland Trail Blazers",
44 "Boston Celtics",
45 "Golden State Warriors",
46 "Boston Celtics",
47 "New York Knicks",
48 "L.A. Lakers",
49 "Milwaukee Bucks",
50 "New York Knicks",
51 "Boston Celtics",
52 "Boston Celtics",
53 "Philadelphia 76ers",
54 "Boston Celtics",
55 "Boston Celtics",
56 "Boston Celtics",
57 "Boston Celtics",
58 "Boston Celtics",
59 "Boston Celtics",
60 "Boston Celtics",
61 "Boston Celtics",
62 "St. Louis Hawks",
63 "Boston Celtics",
64 "Philadelphia Warriors",
65 "Syracuse Nats",
66 "Minneapolis Lakers",
67 "Minneapolis Lakers",
68 "Minneapolis Lakers",
69 "Rochester Royals",
70 "Minneapolis Lakers",
71 "Minneapolis Lakers",
72 "Baltimore Bullets",
73 "Philadelphia Warriors",
74 };
75 int main(){
76     int T,kase = 1;
77     cin >> T;
78     getchar();
79     while(T --){
80         int cnt = 0;
81         string str;
82         getline(cin,str);
83         for(int i = 0 ; i < 70 ; i ++){
84             if(str == s[i]) cnt ++;
85         }
86         printf("Case #%d: %d\n",kase ++,cnt);
87     }
88     return 0;
89 }
View Code

 

  1012,我们一开始以为是个技巧题(只要想到了方法就能过),因为看我们学校其他队都过了这题然而我们却卡了很久,我们以为是很简单的- -结果就没想着用线段树去写。。没想到题解真的是用树状数组维护的- -!这个题目的关键是怎么找出一个数字右边有多少个比它小的数字,并且不能用n^2来实现。一开始用的set实现,结果set根本没有迭代器相减的功能!于是就“顺理成章”的卡了很久。。最后终于是用线段树成段更新写出来了。我的思路大致是这样的,找一个数右边有多少个比它小的数,那么我从左边开始扫描,对一个数来讲,比方说5,那么它右边最多只有4个数字比它小,但是如果5的左边已经出现了一个比它小的数,那么5右边比它小的数字的个数将减少1,那么我们怎么使用线段树呢?比方说左边过来是4,5,6,先扫描到4,那么比4大的区间(即[5,n])的懒惰标记都加1,表示的是比它大的数字的右边的比它们小的数的个数减少了1(例如这里,5和6因为4比它们小,所以右边的比它们小的数的个数显然都减少了一个),那么我再扫描到5的时候,5右边比它小的数的个数就是(5-1)-add[5]=4-1=3了。

  那么顺便再讲下这题最后的思路,我们当时讨论的是某个数的答案应该等于(这个数字i,它的位置pos[i],pos[i]+d[i])这三个数字中,两两差值最大的一个(其中d[i]表示的是i这个数字右边比它小的数的个数)。pos[i]+d[i]是什么意思呢?因为“考虑一个位置上的数字c在冒泡排序过程的变化情况。c会被其后面比c小的数字各交换一次,之后c就会只向前移动”,所以pos[i]+d[i]就是其移动的一个右边的位置(事实上是最右边的位置,为什么是最右边后面再讲)。这样代码就可以写出来了,现场的代码如下:

  1 #include<cstdio>
  2 #include<bits/stdc++.h>
  3 #define t_mid (l+r >> 1)
  4 #define ls (o<<1)
  5 #define rs (o<<1 | 1)
  6 #define lson ls,l,t_mid
  7 #define rson rs,t_mid+1,r
  8 
  9 using namespace std;
 10 const int N = 1e5+10;
 11 int num[N];
 12 int d[N];
 13 int pos[N];
 14 
 15 int c[N<<2],add[N<<2];
 16 
 17 void up(int o) {c[o] = c[ls] + c[rs];}
 18 void down(int o,int len)
 19 {
 20     if(add[o])
 21     {
 22         add[ls] += add[o];
 23         add[rs] += add[o];
 24         c[ls] += add[o] * (len - (len >> 1) );
 25         c[rs] += add[o] * (len >> 1);
 26         add[o]=0;
 27     }
 28 }
 29 int build(int o,int l,int r)
 30 {
 31     if(l==r) return c[o]=0;
 32     return c[o] = build(lson) + build(rson);
 33 }
 34 void update(int o,int l,int r,int ql,int qr,int dt)
 35 {
 36     //printf("%d %d %d %d %d !!\n",o,l,r,ql,qr);
 37     if(ql == l && qr == r)
 38     {
 39         add[o] += dt;
 40         c[o] += dt * (r-l+1);
 41         return;
 42     }
 43     down(o,r-l+1);
 44     if(qr <= t_mid) update(lson,ql,qr,dt);
 45     else if(ql>t_mid) update(rson,ql,qr,dt);
 46     else
 47     {
 48         update(lson,ql,t_mid,dt);
 49         update(rson,t_mid+1,qr,dt);
 50     }
 51     up(o);
 52 }
 53 int query(int o,int l,int r,int ql,int qr)
 54 {
 55     if(ql == l && qr == r) return c[o];
 56     down(o,r-l+1);
 57     int res = 0;
 58     if(qr <= t_mid) res+=query(lson,ql,qr);
 59     else if(ql>t_mid) res+=query(rson,ql,qr);
 60     else
 61     {
 62         res+=query(lson,ql,t_mid);
 63         res+=query(rson,t_mid+1,qr);
 64     }
 65     return res;
 66 }
 67 void init()
 68 {
 69     memset(add,0,sizeof(add));
 70 }
 71 
 72 
 73 int main()
 74 {
 75     int T;
 76     scanf("%d",&T);
 77     int kase = 1;
 78     while(T--)
 79     {
 80         int n;
 81         scanf("%d",&n);
 82         for(int i= 1;i <= n;i ++)
 83             {scanf("%d",&num[i]);pos[num[i]]=i;}
 84         memset(d,0,sizeof(d));
 85         //d[n] = 0;
 86 
 87         build(1,1,n);
 88         init();
 89 
 90         for(int i=1;i<=n;i++)
 91         {
 92             int t = num[i];
 93             //printf("%d !\n",t);
 94             if(t<n) update(1,1,n,t+1,n,1);
 95             d[t] = t-1 - (query(1,1,n,t,t));
 96         }
 97 
 98 
 99         //printf("!!#### %d \n",d[5]);
100 
101         printf("Case #%d: ",kase++);
102         for(int i=1;i<=n;i++)
103         {
104             //printf("%d%c",max(abs(pos[i]-i),d[i]),i==n?'\n':' ');
105             int aa = i,bb=pos[i],cc= pos[i]+d[i];
106             int aaa=abs(aa-bb),bbb=abs(aa-cc),ccc=abs(bb-cc);
107             printf("%d%c",max(aaa,max(bbb,ccc)),i==n?'\n':' ');
108         }
109     }
110 }
View Code

接着我们继续讨论上面的问题,显然的左边的位置应该是min(i,pos[i]),那么pos[i]+d[i]和 i 是谁比较大一点呢?如果pos[i]>=i,那么肯定前者大,现在我们考虑pos[i]<i的情况,也就是说 i 这个数一开始被放在了它正确的位置的左边,我们为了让前者更小,就让d[i]更小,那么 i 这个数的右边应该尽量使比它大的数,但是即便是这样也只能在 i 正确的位置之后才有可能填充满比它大的数,如果这样那么至少从pos[i]+1一直到 i 这么多的位置上都是比 i 小的数,换言之,d[i]>=i-pos[i],移项得pos[i]+d[i]>=i,即前者大,举个例子好了,比方说7我把它放在了7号位置以前,假设比它大的都放在后面了,这样d[7]会更小一点,那么假设7放在了5号位,那么6,7都是比7小的数字,放在了x号位,那么x+1号位一直到7号位都是比7小的数字,即d[7]>=7-x=7-pos[7]。很显然就是上面的结论了。

  所以说最左边的位置是min(i,pos[i]),最右边的位置是pos[i]+d[i]。

  这样的话代码可是得到一点优化,修改后的代码如下:

  1 #include<cstdio>
  2 #include<bits/stdc++.h>
  3 #define t_mid (l+r >> 1)
  4 #define ls (o<<1)
  5 #define rs (o<<1 | 1)
  6 #define lson ls,l,t_mid
  7 #define rson rs,t_mid+1,r
  8 
  9 using namespace std;
 10 const int N = 1e5+10;
 11 int num[N];
 12 int d[N];
 13 int pos[N];
 14 
 15 int c[N<<2],add[N<<2];
 16 
 17 void up(int o) {c[o] = c[ls] + c[rs];}
 18 void down(int o,int len)
 19 {
 20     if(add[o])
 21     {
 22         add[ls] += add[o];
 23         add[rs] += add[o];
 24         c[ls] += add[o] * (len - (len >> 1) );
 25         c[rs] += add[o] * (len >> 1);
 26         add[o]=0;
 27     }
 28 }
 29 int build(int o,int l,int r)
 30 {
 31     if(l==r) return c[o]=0;
 32     return c[o] = build(lson) + build(rson);
 33 }
 34 void update(int o,int l,int r,int ql,int qr,int dt)
 35 {
 36     if(ql == l && qr == r)
 37     {
 38         add[o] += dt;
 39         c[o] += dt * (r-l+1);
 40         return;
 41     }
 42     down(o,r-l+1);
 43     if(qr <= t_mid) update(lson,ql,qr,dt);
 44     else if(ql>t_mid) update(rson,ql,qr,dt);
 45     else
 46     {
 47         update(lson,ql,t_mid,dt);
 48         update(rson,t_mid+1,qr,dt);
 49     }
 50     up(o);
 51 }
 52 int query(int o,int l,int r,int ql,int qr)
 53 {
 54     if(ql == l && qr == r) return c[o];
 55     down(o,r-l+1);
 56     int res = 0;
 57     if(qr <= t_mid) res+=query(lson,ql,qr);
 58     else if(ql>t_mid) res+=query(rson,ql,qr);
 59     else
 60     {
 61         res+=query(lson,ql,t_mid);
 62         res+=query(rson,t_mid+1,qr);
 63     }
 64     return res;
 65 }
 66 void init()
 67 {
 68     memset(add,0,sizeof(add));
 69 }
 70 
 71 
 72 int main()
 73 {
 74     int T;
 75     scanf("%d",&T);
 76     int kase = 1;
 77     while(T--)
 78     {
 79         int n;
 80         scanf("%d",&n);
 81         for(int i= 1;i <= n;i ++)
 82             {scanf("%d",&num[i]);pos[num[i]]=i;}
 83         memset(d,0,sizeof(d));
 84 
 85         build(1,1,n);
 86         init();
 87 
 88         for(int i=1;i<=n;i++)
 89         {
 90             int t = num[i];
 91             if(t<n) update(1,1,n,t+1,n,1);
 92             d[t] = t-1 - (query(1,1,n,t,t));
 93         }
 94 
 95         printf("Case #%d: ",kase++);
 96         for(int i=1;i<=n;i++)
 97         {
 98             int left = min(i,pos[i]),right = pos[i]+d[i];
 99             printf("%d%c",right - left,i==n?'\n':' ');
100         }
101     }
102 }
View Code

  理解透了的话,这道题还是相当有意思的~