题目名称 | 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 2000000using 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
typelink=^treap;treap=recordlch,rch:link;lnum,rnum,data,heap:longint;end;vartree:link;n,i,j,x,ma:longint;ans:int64;
procedure right(var tree:link);varp:link;beginp:=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);varp:link;beginp:=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);beginif (tree=nil) then
beginnew(tree);
tree^.lch:=nil;tree^.rch:=nil;tree^.lnum:=0;tree^.rnum:=0;tree^.data:=x;tree^.heap:=random(maxlongint);endelse
if (tree^.data>x) then
begininc(tree^.lnum);insert(tree^.lch,x);//if (tree^.lch^.heap<tree^.heap) then right(tree);
endelse
begininc(tree^.rnum);insert(tree^.rch,x);//if (tree^.rch^.heap<tree^.heap) then left(tree);
end;end;procedure searchmin(tree:link;x:longint);beginif (tree=nil) then exit;
if (tree^.data>x) then
beginsearchmin(tree^.lch,x);endelse
if (tree^.data=x) then
beginj:=x;exit;endelse
beginif (j<tree^.data) then j:=tree^.data;
searchmin(tree^.rch,x);end;end;procedure searchmax(tree:link;x:longint);beginif (tree=nil) then exit;
if (tree^.data<x) then
beginsearchmax(tree^.rch,x);endelse
if (tree^.data=x) then
beginj:=x;exit;endelse
beginif (j>tree^.data) then j:=tree^.data;
searchmax(tree^.lch,x);end;end;beginassign(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 dobeginreadln(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; |