2016_Matrix_Warm_Up_2

http://acm.hust.edu.cn/vjudge/contest/130363#overview

 

比赛

 

A 题意:每个题目有难度1-10, 可能被泄露, 以及是否通过.  求多少对可疑记录,  可疑记录对定义为 泄露的题目通过了,却没通过难度低,没泄露的题目.

解法: on统计每个难度1-10有多少题满足没泄露且没通过,  然后再on遍历,对每个泄露且通过的题目, 所有难度比他低的记录都能构成一对答案,累加.

 

 1 //#define txtout
 2 //#define debug
 3 #include<bits/stdc++.h>
 4 #define mt(a,b) memset(a,b,sizeof(a))
 5 using namespace std;
 6 typedef long long LL;
 7 const double pi=acos(-1.0);
 8 const double eps=1e-8;
 9 const int inf=0x3f3f3f3f;
10 const int M=1e5+10;
11 int n;
12 struct G{
13     int d,flag;
14     char c[4];
15 }g[M];
16 LL sum[16];
17 void init(){
18     mt(sum,0);
19     for(int i=0;i<n;i++){
20         if(!g[i].flag&&g[i].c[0]=='i'){
21             sum[g[i].d]++;
22         }
23     }
24     for(int i=2;i<=10;i++){
25         sum[i]+=sum[i-1];
26     }
27 }
28 LL solve(){
29     init();
30     LL answer=0;
31     for(int i=0;i<n;i++){
32         if(g[i].flag&&g[i].c[0]=='c'){
33             answer+=sum[g[i].d-1];
34         }
35     }
36     return answer;
37 }
38 int main(){
39     #ifdef txtout
40     freopen("in.txt","r",stdin);
41     freopen("out.txt","w",stdout);
42     #endif // txtout
43     int t;
44     scanf("%d",&t);
45     while(t--){
46         scanf("%d",&n);
47         for(int i=0;i<n;i++){
48             scanf("%d%d%s",&g[i].d,&g[i].flag,g[i].c);
49         }
50         printf("%lld\n",solve());
51     }
52     return 0;
53 }
View Code

 

 

 B 题意:给月份和该月第一天是星期几,问该月有几个星期五和星期六。

解法:得到月份和星期,然后遍历每一天统计。

 

 1 //#define txtout
 2 //#define debug
 3 #include<bits/stdc++.h>
 4 #define mt(a,b) memset(a,b,sizeof(a))
 5 using namespace std;
 6 typedef long long LL;
 7 const double pi=acos(-1.0);
 8 const double eps=1e-8;
 9 const int inf=0x3f3f3f3f;
10 const int M=1e5+10;
11 char month[32][32]={"JAN","FEB","MAR","APR","MAY","JUN","JUL","AUG","SEP","OCT","NOV","DEC"};
12 int day[32]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30 ,31};
13 char weekday[32][32]={"SUN","MON","TUE","WED","THU","FRI","SAT"};
14 char a[M];
15 char b[M];
16 int getMonth(){
17     for(int i=0;i<12;i++){
18         if(!strcmp(a,month[i])) return i;
19     }
20     return -1;
21 }
22 int getWeek(){
23     for(int i=0;i<7;i++){
24         if(!strcmp(b,weekday[i])) return i;
25     }
26     return -1;
27 }
28 int solve(){
29     int m=getMonth();
30     int w=getWeek();
31     int d=day[m];
32     int sum=0;
33     for(int i=1;i<=d;i++){
34         if(w==5||w==6){
35             sum++;
36         }
37         w=(w+1)%7;
38     }
39     return sum;
40 }
41 int main(){
42     #ifdef txtout
43     freopen("in.txt","r",stdin);
44     freopen("out.txt","w",stdout);
45     #endif // txtout
46     int t;
47     scanf("%d",&t);
48     while(t--){
49         scanf("%s%s",a,b);
50         printf("%d\n",solve());
51     }
52     return 0;
53 }
View Code

 

 

C  等待补充。

 

D 题意:给一个字符串,问构造一个与其长度一样的串,最长公共子序列的长度最短为多少。

解法: 贪心的选取一个原串出现次数最少的字符,全用这个字符构成一个串,最长公共子序列的长度就是这个字符在原串中出现次数。

 

 1 //#define txtout
 2 //#define debug
 3 #include<bits/stdc++.h>
 4 #define mt(a,b) memset(a,b,sizeof(a))
 5 using namespace std;
 6 typedef long long LL;
 7 const double pi=acos(-1.0);
 8 const double eps=1e-8;
 9 const int inf=0x3f3f3f3f;
10 const int M=1e5+10;
11 char a[M];
12 int sum[32];
13 int solve(){
14     mt(sum,0);
15     for(int i=0;a[i];i++){
16         sum[a[i]-'a']++;
17     }
18     int answer=inf;
19     for(int i=0;i<26;i++){
20         answer=min(answer,sum[i]);
21     }
22     return answer;
23 }
24 int main(){
25     #ifdef txtout
26     freopen("in.txt","r",stdin);
27     freopen("out.txt","w",stdout);
28     #endif // txtout
29     int t,cas=1;
30     scanf("%d",&t);
31     while(t--){
32         scanf("%s",a);
33         printf("Case %d: %d\n",cas++,solve());
34     }
35     return 0;
36 }
View Code

 

 

E 题意:输入n,求2-n所有F(i)的和, F[i]是 i 所有因子的和。

解法:枚举每个数,其倍数的位置都加上这个数,这个的复杂度是 n/1+n/2+n/3+...+n/n= n*(1/1+1/2+1/3+1/4) ~= n*ln(n).  调和级数。然后再求个前缀和,查询O1。

 

 1 //#define txtout
 2 //#define debug
 3 #include<bits/stdc++.h>
 4 #define mt(a,b) memset(a,b,sizeof(a))
 5 using namespace std;
 6 typedef long long LL;
 7 const double pi=acos(-1.0);
 8 const double eps=1e-8;
 9 const int inf=0x3f3f3f3f;
10 const int M=2e7+10;
11 int n;
12 LL a[M];
13 void init(){
14     for(int i=2;i<M;i++){
15         a[i]=1;
16     }
17     for(int i=2;i<M;i++){
18         for(int j=i;j<M;j+=i){
19             a[j]+=i;
20         }
21     }
22     for(int i=3;i<M;i++){
23         a[i]+=a[i-1];
24     }
25 }
26 int main(){
27     #ifdef txtout
28     freopen("in.txt","r",stdin);
29     freopen("out.txt","w",stdout);
30     #endif // txtout
31     init();
32     while(~scanf("%d",&n),n){
33         printf("%lld\n",a[n]);
34     }
35     return 0;
36 }
View Code

 

 F 题意: 从n*m的矩阵中,选取一个平行于边界的子矩阵, 子矩阵的值表示高度, 他们会选一个点开party, 这个点满足所有点到这个点的距离和最小,如果多个点满足条件,会选取高度最高的. 选出这个点以后, 和查询的h比较,如果这个点的高度<h,party取消. 查询h, 对每个h , 输出能选出的最大面积的子矩形, 满足上述约束.

 

解法: 暴力, 枚举出一个子矩阵, n^4, 判断是否满足约束,满足就更新答案。  判断, 暴力的话枚举找开party的点,依次和所有点做差, 得到距离和最小的,相同选高度最高的, n^4, 然后判断 是否<h.  

T*Q*n^8。

第一步优化:优化判断,将所有子矩阵的点排序,对于最大Hn的和最小H1的两个点来说,选取任意一个点Hi,他们两个的距离和  Hn-Hi  + Hi-H1  都等于他们的距离 Hn - H1 ,那么这两个元素就可以消去了,明显贪心的选取中间的会更好,  如果选两个元素之外的, 距离明显增加。  以此类推, 相当于求中位数的过程。 当n 为奇数时, 选取的点 唯一,  当n 为偶数时, 根据题意选取 h大的。

所以 , 对于查询 h 来说, 一个子矩阵是否满足约束, 条件转化为, 该子矩阵中位数(偶数时是中间两个数较大的那个数,不是数学定义里的中位数)要 〉=h。再进一步转化,子矩阵中〉=h的个数要超过一半。

当n为偶数时,(大于等于h的个数)要〉=n/2。 当n为奇数的时候(大于等于h的个数)要〉=n/2+1. 

此时,对于一个查询 h ,可以n^2预处理, 将〉=h的点设为1,将<h 的设为0。然后n^2预处理出sum[x][y],表示 (1,1)到(x,y)子矩阵的和。 然后n^4枚举子矩阵,O1判断,判断方法就是求该子矩阵1的个数是否》=总个数/2, 求任意一个子矩阵的和, 可以容斥原理, sum[x2][y2] - sum[x2][y1-1] -sum[x1-1][y2] + sum[x1-1][y1-1];

目前复杂度只剩枚举子矩阵,  T*Q*n^4。  但还是超时。 还要优化 。

先做一个预处理,〉=h设置为1,<h设置为-1 , 上述约束转化为子矩阵的和>=0。

首先n^2枚举出子矩阵的左边界和右边界,然后用一维数组 s[i] 记录 第 i 行在界内的和。 枚举行on, 用前缀和o1求, 这里的复杂度是 on3。

问题转化为了, 在s这个一维数组中,找到一段连续的子段,在满足子段和>=0的情况下,使得子段尽可能长。因为这样面积就大了。

处理这个问题,n2枚举起点终点,用前缀和o1判合法, 是可行的, 但是复杂度就回到 n4了。

设起点x+1,终点y,   约束就是   S[y]-S[x]>=0 ,  子段长度就是  y - x,

我们考虑处理出单调序列,然后双指针。

开始时序列为空,从前往后枚举S[i] , 作为 S[x],  当序列为空时,将s[ i ]加入序列, 当序列不为空时, 只有s[i] 的值小于序列最后一个元素的值,才加入序列尾部。

原因是,我们希望x越小越好, sx也越小越好,这样更有可能满足条件,也更有可能更新答案,  那么当来一个元素 , 我们尝试用它作为sx时, 因为我们从前往后枚举, 所以x显然已经比之前的大了,只有sx比之前的小,才有可能找到更好的解,否则之前的解一定更好。

经过这次on,我们处理出了一个sx序列,序号x递增,值sx递减的序列。

同理,我们从后往前枚举si, 作为sy。 当序列为空时, 加入, 当不为空时, 只有当 sy的值大于序列最后一个元素的值,才加入序列尾部。

原因是,我们希望y越大越好, sy也越小越好,这样更有可能满足条件,也更有可能更新答案,  那么当来一个元素 , 我们尝试用它作为sy时, 因为我们从后往前枚举, 所以y显然已经比之前的小了,只有sy比之前的大,才有可能找到更好的解,否则之前的解一定更好。  (多目标优化的非支配解集,这个序列实际上)

经过这次on,我们处理出了一个sy序列,序号y递减,值sy递增的序列。

下面我们用两个指针来枚举解,复杂度2 n。

i 枚举sy中每一个元素, j开始时停在sx的尾部。

对于每个i ,  j一开始在sx 最小的地方,

如果满足,就更新答案,j--。 因为当前满足了, 所以我们尝试让sx 增大一点, 这样x就减少了,如果还〉=0,那还能进一步更新答案,如果不合法了,说明我们已经枚举完 i 结尾的所有情况了。

当i++的时候 j停在原地, 所以能2n, 停在原地是对的的原因是, i++导致sy增大, 那么 sx可能不变,也可能增大,不会去减小,减小使得x增加,对答案没有好处。

 

 单调的两个指针  使得这步复杂度on, 整体TQn3

 

  1 //#define txtout
  2 //#define debug
  3 #include<bits/stdc++.h>
  4 #define mt(a,b) memset(a,b,sizeof(a))
  5 using namespace std;
  6 typedef long long LL;
  7 const double pi=acos(-1.0);
  8 const double eps=1e-8;
  9 const int inf=0x3f3f3f3f;
 10 const int M=1e3+10;
 11 int n,m,lq;
 12 int a[M][M];
 13 int b[M][M];
 14 int sum[M][M];
 15 int q[M];
 16 int answer[M];
 17 int buffer[M];
 18 typedef pair<int,int> pii;
 19 pii tail[M],head[M];
 20 void init(int h){
 21     for(int i=0;i<n;i++){
 22         for(int j=0;j<m;j++){
 23             b[i][j]=1;
 24             if(a[i][j]<h){
 25                 b[i][j]=-1;
 26             }
 27             sum[i][j]=b[i][j];
 28             if(j){
 29                 sum[i][j]+=sum[i][j-1];
 30             }
 31         }
 32     }
 33 }
 34 int get_buffer(){
 35     for(int i=1;i<n;i++){
 36         buffer[i]+=buffer[i-1];
 37     }
 38     if(buffer[n-1]>=0) return n;
 39     int answer=0;
 40     int len_tail=0;
 41     for(int i=n-1;i>=0;i--){
 42         if(buffer[i]>=0){
 43             answer=max(answer,i+1);
 44         }
 45         int s=len_tail;
 46         if(s==0){
 47             tail[len_tail++]=make_pair(buffer[i],i);
 48             continue;
 49         }
 50         if(buffer[i]>tail[s-1].first){
 51             tail[len_tail++]=make_pair(buffer[i],i);
 52         }
 53     }
 54     int len_head=0;
 55     for(int i=0;i<n;i++){
 56         int s=len_head;
 57         if(s==0){
 58             head[len_head++]=make_pair(buffer[i],i);
 59             continue;
 60         }
 61         if(buffer[i]<head[s-1].first){
 62             head[len_head++]=make_pair(buffer[i],i);
 63         }
 64     }
 65     for(int i=0,j=len_head-1;i<len_tail&&j>=0;i++){
 66         if(tail[i].second<=answer) break;
 67         while(j>=0&&tail[i].first-head[j].first>=0){
 68             answer=max(answer,tail[i].second-head[j].second);
 69             j--;
 70         }
 71     }
 72     return answer;
 73 }
 74 int get(int h){
 75     init(h);
 76     int answer=0;
 77     for(int x=0;x<m;x++){
 78         for(int y=x;y<m;y++){
 79             if(answer>=(y-x+1)*n) continue;
 80             for(int i=0;i<n;i++){
 81                 int value=sum[i][y];
 82                 if(x){
 83                     value-=sum[i][x-1];
 84                 }
 85                 buffer[i]=value;
 86             }
 87             answer=max(answer,(y-x+1)*get_buffer());
 88         }
 89     }
 90     return answer;
 91 }
 92 void solve(){
 93     for(int i=0;i<lq;i++){
 94         answer[i]=get(q[i]);
 95     }
 96 }
 97 int main(){
 98     #ifdef txtout
 99     freopen("in.txt","r",stdin);
100     freopen("out.txt","w",stdout);
101     #endif // txtout
102     int t,cas=1;
103     scanf("%d",&t);
104     while(t--){
105         scanf("%d%d",&n,&m);
106         for(int i=0;i<n;i++){
107             for(int j=0;j<m;j++){
108                 scanf("%d",&a[i][j]);
109             }
110         }
111         scanf("%d",&lq);
112         for(int i=0;i<lq;i++){
113             scanf("%d",&q[i]);
114         }
115         solve();
116         printf("Case %d:\n",cas++);
117         for(int i=0;i<lq;i++){
118             printf("%d\n",answer[i]);
119         }
120     }
121     return 0;
122 }
View Code

 

 

end

 

posted @ 2016-08-29 10:06  cs1131  阅读(190)  评论(0编辑  收藏  举报