2024年3月7日发(作者:)

NOIP2013年普及组复赛真题解析

T1 计数问题

解析:

对于 100%的数据,1≤ n ≤ 1,000,000,0 ≤ x ≤ 9。枚举每个数的每一位就行。

程序:

include

#include

using namespace std;

int main(){

int n,x;

cin>>n>>x;

int i,c=0;

for(i=1;i<=n;i++){

int a=i;

while(a!=0){

if(a%10==x)c++;

a/=10;

}

}

cout<

return 0;

}

===================================================

T2 表达式求值

解析:

对于 30%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100;

对于 80%的数据,0≤表达式中加法运算符和乘法运算符的总数≤1000;

对于 100%的数据,0≤表达式中加法运算符和乘法运算符的总数≤100000。

程序:

#include

#include

#include

#include

#include

using namespace std;

char last;

char c;

int x=0;

int a=0,b=1;

int sum=0;

int main(){

int i,j;

bool flag=1;

do{

if(cin>>c);

else{

flag=0;

c='+';//相当于在整个串最后补个+号,以完成全部运算

}

if(c>='0' && c<='9')x=x*10+c-'0';//读取数

else{

a=x;//如果读到的不是数字,把之前读到的数存起来

x=0;//初始化

}

if(c=='*'){//处理乘号,方法是先记下这个数,下次读到乘号再计算

last=1;

b=(a*b)%10000;//有连续乘号时,累乘

}

if(c=='+'){

if(last){//上一个是乘号的情况

a=(a*b)%10000;

sum=(sum+a)%10000;

b=1;

last=0;

}

else sum+=a;//上一个是加号的情况

}

}while(flag==1);

printf("%d",sum%10000);

return 0;

}

===================================================

T3

解析:

Case 1:

小朋友的特征值分别为 1、3、6、10、15,分数分别为 1、2、5、11、21,最大值 21对 997 的模是 21。

Case 2:

小朋友的特征值分别为-1、-1、-1、-1、-1,分数分别为-1、-2、-2、-2、-2,最大值-1 对 7 的模为-1,输出-1。

对于 50%的数据,1 ≤ n ≤ 1,000,1 ≤ p ≤ 1,000所有数字的绝对值不超过 1000;

对于 100%的数据,1 ≤ n ≤ 1,000,000,1 ≤ p ≤ 10^9,其他数字的绝对值均不超过 10^9。

完全可以读一个算一个,每次把新算出的数值和之前保存的最大值比较,得到新的最大值。特征值和分数都这么处理

程序:

#include

#include

#include

#include

using namespace std;

const long long inf=1000000005;

int n,p,a[1000500];

long long su[1000500]={0};//特征值,也可以不要数组

long long scoremx=-inf;//前排最大分数

long long dmx=-inf;//分数加特征值的最大值

long long ans=-inf;

int sum1(){//计算特征值。

int i,j;

long long s=0;

long long mx=-inf;

for(i=1;i<=n;i++){

if(s+a[i]>mx)mx=s+a[i];

su[i]=mx;

if(s+a[i]>0) s+=a[i];

else s=0;

}

//

for(i=1;i<=n;i++){

su[i]%=p<<1;//防止数据过大

//p<<1可以避免mod时的误差(如果是读一个算一个,数据不会超限制 ,而一次算完的做法就有必要处理一下数据了)

}

return 0;

}

int main(){

freopen("","r",stdin);

freopen("","w",stdout);

int i,j;

scanf("%d%d",&n,&p);

for(i=1;i<=n;i++)scanf("%d",&a[i]);

sum1();

long long score;

//1 处理第一个数据

scoremx=su[1];

score=su[1];

dmx=score+su[1];

//end

for(i=2;i<=n;i++){

score=dmx;

if(dmx>scoremx)scoremx=dmx;

if(su[i]+score>dmx)dmx=su[i]+score;

}

printf("%d",scoremx%p);

return 0;

}

===================================================

T4-车站分级

解析:

对于 20%的数据,1 ≤ n, m ≤ 10;

对于 50%的数据,1 ≤ n, m ≤ 100;

对于 100%的数据,1 ≤ n, m ≤ 1000。

如果这趟车次停靠了火车站 x,则始发站、终点站之间所有级别大于等于火车站

x 的都必须停靠,所以一趟车从始发站到终点站之间,没停的站级别都低于停了的站。

在低级站和高级站之间连线,构成AOV网,利用拓扑排序即可知道总共有多少

级车站。

程序:

#include

#include

#include

#include

#include

using namespace std;

int n,m;

int a;//[第i趟车次的停靠站数]

int s[2000];//[第i趟车次停靠的站]

int mp[1200][1200]={0};

int book[1200];

int r[1200],c[1200]; //入度出度

int st[1200];

int ans=0;

void rd(){

scanf("%d%d",&n,&m);

int i,j,k;

for(i=1;i<=m;i++){

memset(book,0,sizeof(book));

scanf("%d",&a);

for(j=1;j<=a;j++){

scanf("%d",&s[j]);

book[s[j]]=1;

}

for(k=s[1];k<=s[a];k++)//遍历从始发站到终点站

{

if(!book[k])

for(j=1;j<=a;j++)

if(!mp[k][s[j]])//从低级连到高级可过,从高级连到低级无误但超时

{

mp[k][s[j]]=1;//从低级站到高级站连线

r[s[j]]++;}//入度++

}

}

}

int main(){

// freopen("","r",stdin);

// freopen("","w",stdout);

rd();

int i,j;

int top=0;

memset(book,0,sizeof(book));

while(1){//拓扑排序

ans++;//循环次数即为总级数

top=0;

for(i=1;i<=n;i++)

if(!r[i] && !book[i])//所有没有入度且之前没处理过的点,都是同一级别的

{

top++;

st[top]=i;//入站

book[i]=1;//标记为已处理

}

if(!top)break;//栈为空,说明所有点都排好序了

for(j=1;j<=n;j++)

for(i=1;i<=top;i++){

if(mp[st[i]][j]){

mp[st[i]][j]=0;

r[j]--;

}

}

if(!top)break;

}

printf("%d",ans-1);

return 0;

}