题目名称

sum

damage

lis

输入文件

sum.in

damage.in

lis.in

输出文件

sum.out

damage.out

lis.out

测试点数目

10

10

10

测试点分数

10

10

10

时间限制

1s

1s

1s

空间限制

128M

128M

128M

NOIP2011热身赛

DAY1

注意事项:

1、C++同学允许使用所有库。

2、Pascal允许使用math,但是需要生成exe提交。

3、题不难,请耐心答题。

4、把握好做每道题的时间,不要出现某一道题目没有提交的情况。

5、注意文件名的书写,字母均为小写。

5、11:00准时交题,11:03之后提交的不予收取。

一、整数划分(sum.pas/c/cpp)

【题目描述】

从文件中读入一个正整数n(10≤n≤31000)。要求将n写成若干个正整数之和,并且使这些正整数的乘积最大。 例如,n=13,则当n表示为4+3+3+3(或2+2+3+3+3)时,乘积=108为最大。

【输入格式】(sum.in):

一个整数,n

【输出格式】(sum.out):

第1行输出一个整数,为最大乘积的位数。 第2行输出最大乘积的前100位,如果不足100位,则按实际位数输出最大乘积。 (提示:在给定的范围内,最大乘积的位数不超过5000位)。

【输入样例】

13

【输出样例】

3

108

 

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;
int n;
int ans[5000];
int a[5000];
void Multity_small(int *a,int b,int *d){
     int c[5000];
     memset(c,0,sizeof(c));
     int len=a[0]+10;
     for (int i=1;i<=len;i++){
         c[i]=a[i]*b;
         c[i]+=c[i-1] / 10000;
         c[i-1] %= 10000;
     }
     while (c[len]==0 && len>1) len--;
     c[0]=len;
     memcpy(d,c,sizeof(c));
}
void Multity_big(int *a,int *b,int *d){
     int c[5000];
     memset(c,0,sizeof(c));
     for (int i=1;i<=a[0];i++){
         for (int j=1;j<=b[0];j++){
             c[i+j-1] +=a[i]*b[j];
             c[i+j] += c[i+j-1] / 10000;
             c[i+j-1] %= 10000;
         }
     }
     c[0]=a[0]+b[0]+1;
     while (c[c[0]]==0 && c[0]>1) --c[0];
     memcpy(d,c,sizeof(c));
}
void power(int *a,int b,int *c){
     int t[5000];
     for (int i=0;i<=a[0];i++) c[i]=t[i]=a[i];
     b--;
     while (b>0){
           if (b & 1) Multity_big(c,t,c);
           b >>= 1;
           Multity_big(t,t,t);
     }
}
void print(int *a){
     printf("%d",a[a[0]]);
     int tot=(int(log10(a[a[0]]))+1);
     int i=0;
     for (i=a[0]-1;i>0;i--){
         if (tot+4>100) break;
         tot+=4;
         if (a[i]<10) printf("000");
         else if (a[i]<100) printf("00");
         else if (a[i]<1000) printf("0");
         printf("%d",a[i]);
     }
     if (i>0 && tot>0){
             if (100-tot==1) printf("%1.1d",a[i]/1000);
             else if (100-tot==2) printf("%2.2d",a[i]/100);
             else if (100-tot==3) printf("%3.3d",a[i]/10);
     }
     putchar('\n');
}
int main(){
    freopen("sum.in","r",stdin);
    freopen("sum.out","w",stdout);
    scanf("%d",&n);
    int t=n / 3;
    int d=n % 3;
    if (d==0){
              a[0]=1;
              a[1]=3;
              power(a,t,ans);
              int p=(ans[0]-1)*4+int(log10(ans[ans[0]]))+1;
              printf("%d\n",p);
              print(ans);
    }else if (d==2){
          a[0]=1;
          a[1]=3;
          power(a,t,ans);
          Multity_small(ans,2,ans);
          int p=(ans[0]-1)*4+int(log10(ans[ans[0]]))+1;
          printf("%d\n",p);
          print(ans);
    }else if (d==1){
          a[0]=1;
          a[1]=3;
          power(a,t-1,ans);
          Multity_small(ans,4,ans);
          int p=(ans[0]-1)*4+int(log10(ans[ans[0]]))+1;
          printf("%d\n",p);
          print(ans);
    }
    return 0;
}

 

二、地震(damage.pas/c/cpp)

【题目描述】

农夫John的农场遭受了一场地震。有一些牛棚遭到了损坏,但幸运地,所有牛棚间的路经都还能使用。 FJ的农场有P(1 <= P <= 30,000)个牛棚,编号1..P, C(1 <= C <= 100,000)条双向路经连接这些牛棚,编号为1..C。 路经i连接牛棚a_i和b_i (1 <= a_i<= P;1 <= b_i <= P),路经可能连接a_i到它自己,两个牛棚之间可能有多条路经。农庄在编号为1的牛棚.,N (1 <= N <= P)头在不同牛棚的牛通过手机短信report_j(2 <= report_j <= P)告诉FJ它们的牛棚(report_j)没有损坏,但是它们无法通过路经和没有损坏的牛棚回到到农场。 当FJ接到所有短信之后,找出最小的不可能回到农庄的牛棚数目。这个数目包括损坏的牛棚。

输入格式

* 第1行: 三个空格分开的数: P, C, 和 N * 第2..C+1行: 每行两个空格分开的数: a_i 和 b_i * 第C+2..C+N+1行: 每行一个数: report_j

输出格式

* 第1行: 一个数,最少不能回到农庄的牛的数目(包括损坏的牛棚).

输入样例

4 3 1
1 2
2 3
3 4
3

输出样例

3

hint

牛棚2遭到损坏,导致牛棚2, 3, 4里面的牛无法回到农庄.

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,c,p;
int x[110000],y[110000];
int v[110000];
int fa[110000];
struct Edge{
       int x,next;
}e[200001];
int first[110000];
int tot;
void add(int x,int y){
     e[++tot].x=y;
     e[tot].next=first[x];
     first[x]=tot;
}
int get(int x){
    if (fa[x]==x) return x;
    return fa[x]=get(fa[x]);
}
void Union(int a,int b){
     int x=get(a);
     int y=get(b);
     if (x!=y) fa[x]=y;
}
int main(){
    freopen("damage.in","r",stdin);
    freopen("damage.out","w",stdout);
    scanf("%d%d%d",&n,&c,&p);
    for (int i=0;i<c;i++){
        scanf("%d%d",&x[i],&y[i]);
        add(x[i],y[i]);
        add(y[i],x[i]);
    }
    for (int i=0;i<p;i++){
        int d;
        scanf("%d",&d);
        for (int t=first[d];t;t=e[t].next){
            v[e[t].x]=true;
        }
    }
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=0;i<c;i++){
        if (!v[x[i]] && !v[y[i]]){
                    Union(x[i],y[i]);
        }
    }
    int ans=0;
    for (int i=2;i<=n;i++){
        if (get(1)!=get(i)) ans++;
    }
    printf("%d\n",ans);
    return 0;
}

 

三、最长上升子序列(lis.pas/c/cpp)

【题目描述】

给出一个长度为N的整数序列,求出包含它的第K个元素的最长上升子序列。

【输入格式】

第一行两个整数N,K

第二行N个整数

【输出格式】

一个整数L如题目所说的序列长度。

【输入样例】

8 6

65 158 170 299 300 155 207 389

【输出样例】

4

【数据范围】

0<N<=200000,0<K<=N

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n,k;
int a[210000];
int q[210000];
int top;
int find(int x){
    if (q[top]<x){
                    top++;
                    return top;
    }
    int l=1,r=top;
    while (l<r){
          int mid=(l+r) >> 1;
          if (q[mid]<x) l=mid+1;else r=mid;
    }
    return l;
}
int main(){
    freopen("lis.in","r",stdin);
    freopen("lis.out","w",stdout);
    scanf("%d%d",&n,&k);
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for (int i=1;i<k;i++)if (a[i]<a[k]){
        if (top==0){
                    q[++top]=a[i];
        }else{
              int j=find(a[i]);
              q[j]=a[i];
        }
    }
    int ans=top;
    top=0;
    for (int i=k+1;i<=n;i++)if (a[i]>a[k]){
        if (top==0){
                    q[++top]=a[i];
        }else{
              int j=find(a[i]);
              q[j]=a[i];
        }
    }
    ans+=top+1;
    printf("%d\n",ans);
    return 0;
}

 

 

NOIP2011热身赛

DAY2

题目名称

Sequence

Min

tree

输入文件

sequence.in

min.in

tree.in

输出文件

sequence.out

min.out

tree.out

测试点数目

10

10

10

测试点分数

10

10

10

时间限制

1s

1s

1s

空间限制

128M

128M

128M

注意事项:

1、C++同学允许使用所有库。

2、Pascal允许使用math,但是需要生成exe提交。

3、题不难,请耐心答题。

4、把握好做每道题的时间,不要出现某一道题目没有提交的情况。

5、注意文件名的书写,字母均为小写。

 

最大子段和变式

给定一串数Ai(-5000=<Ai<=5000),在这一串数种找到不相交的两个子段使其,使其和最大。

输入格式

第一行一个整数n(n<=200000),表明字串中数的个数。

接下来n行,每行一个整数Ai,(-5000=<Ai<=5000);

输出格式

一个数,表示这一串数中不相交的两个最大子段的和。

输入样例

10

1

-1

2

2

3

-3

4

-4

5

-5

输出样例

13

 

#include <cstdio>
#include <iostream>
#define INF 2000000
using namespace std;
int a[300000];
int f[300000];
int n;
int main(){
    freopen("sequence.in","r",stdin);
    freopen("sequence.out","w",stdout);
    scanf("%d",&n);
    int ans=-INF,mymax=-INF,tmp=0;
    for (int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        f[i]=max(f[i-1]+a[i],a[i]);
    }
    for (int i=n;i>1;i--){
        tmp+=a[i];
        if (tmp>mymax) mymax=tmp;
        if (ans<f[i-1]+mymax) ans=f[i-1]+mymax;
        if (tmp<0) tmp=0;
    }
    printf("%d\n",ans);
    return 0;
}

 

最小波动

最小波动是在一串数种出现的一种现象,给定n个数Ai,第i个数的最小波动k=min{|Aj-Ai|}(1=<j<i).而第一个数的最小波动为A1本身。

下面我们给定一串数,让你求出对于每个Ai的最小波动的和。

输入格式

第一行一个整数n(n<=200000),表明字串中数的个数。

接下来n行,每行一个整数Ai,(-32767=<Ai<=32767);

输出格式

一个数sum表示最小波动的和。

输入样例

6

5

1

2

5

4

6

输出格式

12

 

type
  link=^treap;
  treap=record
    lch,rch:link;
    lnum,rnum,data,heap:longint;
  end;
var
  tree:link;
  n,i,j,x,ma:longint;
  ans:int64;
procedure right(var tree:link);
  var
    p:link;
  begin
    p:=tree^.lch;
    tree^.lch:=p^.rch;
    if (p^.rch<>nil) then tree^.lnum:=p^.rnum else tree^.lnum:=0;
    p^.rch:=tree;
    p^.rnum:=tree^.lnum+tree^.rnum+1;
    tree:=p;
  end;
procedure left(var tree:link);
  var
    p:link;
  begin
    p:=tree^.rch;
    tree^.rch:=p^.lch;
    if (p^.lch<>nil) then tree^.rnum:=p^.lnum else tree^.rnum:=0;
    p^.lch:=tree;
    p^.lnum:=tree^.lnum+tree^.rnum+1;
    tree:=p;
  end;
procedure insert(var tree:link;x:longint);
  begin
    if (tree=nil) then
      begin
        new(tree);
        tree^.lch:=nil;
        tree^.rch:=nil;
        tree^.lnum:=0;
        tree^.rnum:=0;
        tree^.data:=x;
        tree^.heap:=random(maxlongint);
      end
    else
      if (tree^.data>x) then
        begin
          inc(tree^.lnum);
          insert(tree^.lch,x);
          //if (tree^.lch^.heap<tree^.heap) then right(tree);
        end
      else
        begin
          inc(tree^.rnum);
          insert(tree^.rch,x);
          //if (tree^.rch^.heap<tree^.heap) then left(tree);
        end;
  end;
procedure searchmin(tree:link;x:longint);
  begin
    if (tree=nil) then exit;
    if (tree^.data>x) then
      begin
        searchmin(tree^.lch,x);
      end
    else
    if (tree^.data=x) then
      begin
        j:=x;
        exit;
      end
    else
      begin
        if (j<tree^.data) then j:=tree^.data;
        searchmin(tree^.rch,x);
      end;
  end;
procedure searchmax(tree:link;x:longint);
  begin
    if (tree=nil) then exit;
    if (tree^.data<x) then
      begin
        searchmax(tree^.rch,x);
      end
    else
    if (tree^.data=x) then
      begin
        j:=x;
        exit;
      end
    else
      begin
        if (j>tree^.data) then j:=tree^.data;
        searchmax(tree^.lch,x);
      end;
  end;
begin
  assign(input,'min.in');
  reset(input);
  assign(output,'min.out');
  rewrite(output);
  readln(n);
  tree:=nil;
  randomize;
  readln(x);
  insert(tree,x);
  ans:=x;
  for i:=2 to n do
    begin
      readln(x);
      j:=-maxlongint;
      ma:=maxlongint;
      searchmin(tree,x);
      if (j<>-maxlongint)and(x-j<ma) then ma:=x-j;
      j:=maxlongint;
      searchmax(tree,x);
      if (j<>maxlongint)and(j-x<ma) then ma:=j-x;
      inc(ans,ma);
      insert(tree,x);
    end;
  writeln(ans);
  close(input);
  close(output);
end.

 

校门外的树

某校大门外长度为L的马路上有一排树,每两棵相邻的树之间的间隔都是1米。我们可以把马路看成一个数轴,马路的一端在数轴0的位置,另一端在L的位置;数轴上的每个整数点,即0,1,2,……,L,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式

输入的第一行有两个整数L(1 <= L <= 2亿)和 M(1 <= M <= 20000),L代表马路的长度,M代表区域的数目,L和M之间用一个空格隔开。接下来的M行每行包含两个不同的整数,用一个空格隔开,表示一个区域的起始点和终止点的坐标。

输出格式

输出包括一行,这一行只包含一个整数,表示马路上剩余的树的数目

输入样例

500 3

150 300

100 200

470 471

输出样例

298

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;
struct node{
       int x,y;
       bool operator <(const node &T)const{
            return (T.x>x) || (T.x==x && T.y>y);
       }
}a[21000];
int n,l;
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%d%d",&l,&n);
    for (int i=1;i<=n;i++){
        scanf("%d%d",&a[i].x,&a[i].y);
    }
    sort(a+1,a+1+n);
    int last=-1,tot=0;
    for (int i=1;i<=n;i++){
        if (a[i].y>last){
                         tot+=a[i].y-max(a[i].x,last)+1;
                         last=a[i].y+1;
        }
    }
    int ans=l+1-tot;
    printf("%d\n",ans);
    return 0;
}