第十一届北京师范大学程序设计竞赛解题报告

 

 A题:模拟

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
    int i,n,cas;
    vector <string> month[15];
    month[11].push_back("Basic Training");
    month[12].push_back("Basic Training");
    month[12].push_back("Rookie Contest");
    for(i=2;i<=4;i++) month[i].push_back("Spring Training");
    month[4].push_back("BNU Contest");
    month[7].push_back("Practice Week");
    for(i=7;i<=8;i++) month[i].push_back("Summer Training");
    for(i=9;i<=11;i++) month[i].push_back("Regional Contest");
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d",&n);
        if(month[n].size()) {
            for(i=0;i<month[n].size();i++)
                printf("%s\n",month[n][i].c_str());
        }else printf("Unknown\n");
    }
    return 0;
}


 

B题:背包,二分都可以,背包的代码如下

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int dp[5][1100];
int main()
{
    int cas,L,x,n,i,j,k,v;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d%d%d",&L,&x,&n);
        L-=x; memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&v);
            for(k=0;k<4;k++)
                for(j=L-v;j>=0;j--)
                    if(dp[k][j]) dp[k+1][j+v]=1;
        }
        puts(dp[4][L]?"Yes":"No");
    }
    return 0;
}


C题:直接暴力搜索必胜点,必败点;

#include<stdio.h>
#include<string.h>
#include<string>
#include<map>
#include<algorithm>
#include<vector>

using namespace std;
int dfs(int L,int H)
{
    if(L==1&&H==1) return 0;
    if(L%2==0&&dfs(L/2,H)==0)
        return 1;
    if(H%2==0&&dfs(L,H/2)==0)
        return 1;
    return 0;
}
int main()
{
    int cas,L,H;
    scanf("%d",&cas);
    while(cas--){
        scanf("%d%d",&L,&H);
        puts(dfs(L,H)?"Adidas loses":"Adivon prevails");
    }
    return 0;
}


 

D题:构造trie树,找分叉点并统计,用所有的分叉点统计值求和就是答案;

#include<stdio.h>
#include<string.h>
#include<string>
#include<map>
#include<set>
#include<algorithm>
#include<vector>
using namespace std;
#define N 210000
int ch[N][26];
long long ant,val[N];

int newnode(){
    int i;++ant;
    for(i=0;i<26;i++)
        ch[ant][i]=0;
    val[ant]=0;
    return ant;
 }
void insert(char *str)
{
    int idx,i,rt=0;
    val[rt]++;
    for(i=0;str[i];i++)
    {
        idx=str[i]-'a';
        if(!ch[rt][idx]) ch[rt][idx]=newnode();
        rt=ch[rt][idx];
        val[rt]++;
    }
}
long long ans;
void make(int rt)
{
    int i;
    long long t;
    for(i=0;i<26;i++){
        if(ch[rt][i]){
            make(ch[rt][i]);
            t=val[0]-val[ch[rt][i]];
            t*=val[ch[rt][i]];
            ans+=t;
        }
    }
}
char str[110000];
int main()
{
    int cas,i,j,L,n;
    scanf("%d",&cas);
    while(cas--)
    {
        ant=-1;newnode();
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%s",str);
            L=strlen(str);
            insert(str);
            for(j=0;j<L/2;j++)
                swap(str[j],str[L-j-1]);
            insert(str);
        }
        ans=0;make(0);
        printf("%lld\n",ans);
    }
    return 0;
}

直接模拟统计的版本:

#include<stdio.h>
#include<string.h>
#include<string>
#include<map>
#include<set>
#include<algorithm>
#include<vector>
using namespace std;


#define N 210000

int ch[N][26],flag[N];
long long ant,val[N],sum[N],ans;

int newnode(){
    int i;++ant;
    for(i=0;i<26;i++)
        ch[ant][i]=0;
    val[ant]=0;flag[ant]=0;
    return ant;
 }
void insert(char *str)
{
    int idx,i,rt=0;
    val[rt]++;
    for(i=0;str[i];i++)
    {
        idx=str[i]-'a';
        if(!ch[rt][idx]) ch[rt][idx]=newnode();
        rt=ch[rt][idx];
        val[rt]++;
    }
	flag[rt]++;
}
void make(int rt)
{
    int i;
	sum[rt]=0;
    for(i=0;i<26;i++){
        if(ch[rt][i]){
            make(ch[rt][i]);
            sum[rt]+=sum[ch[rt][i]]+val[ch[rt][i]];
        }
    }
	for(i=0;i<26;i++){
		if(ch[rt][i]){
            	ans+=val[ch[rt][i]]*(sum[rt]-sum[ch[rt][i]]-val[ch[rt][i]]);
			if(flag[ch[rt][i]])ans+=sum[ch[rt][i]]*flag[ch[rt][i]];
		}
	}
}
char str[110000];
int main()
{
    int cas,i,j,L,n;
    scanf("%d",&cas);
    while(cas--)
    {
        ant=-1;newnode();
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%s",str);
            L=strlen(str);
            insert(str);
            for(j=0;j<L/2;j++)
                swap(str[j],str[L-j-1]);
            insert(str);
        }
        ans=0;make(0);
        printf("%lld\n",ans);
    }
    return 0;
}



 

 

E题:不会弄

 

F题:比赛的时候没弄出来,赛后看了别人的才弄出来,大概思路是:

设last表示在当前点可获得的分的期望值,则下一点可获得的分的期望值就是last=(last+1)*p

最后把所有点上获得的分加起来就是答案;

 

G题:不会弄

 

H题:求出公平决策事件发生的概率p,最后决策事件发生的期望次数就是1/p;

#include<stdio.h>
#include<string.h>
#include<string>
#include<algorithm>
#include<vector>

using namespace std;
int main()
{
    int cas;
    double p;
    scanf("%d",&cas);
    while(cas--){
        scanf("%lf",&p);
        printf("%.2lf\n",1.0/(p*(1-p)));
    }
    return 0;
}


 

I题:和逆元的性质有关,因为除以一个数就等于乘以一个数的逆元,b[i]=(a[i]*w)%m且v是w模m的逆;

所以a[i]=(b[i]*v)%m;也就有s*v=sum{ (a[i]*x[i]) }%m; 又因为a[i]>2*a[i-1],下面的过程就和二进制分解一样了,把所有的x[i]待定出来就可以了;

 

 

J题:鸣人的查克拉

题目链接:http://acm.bnu.edu.cn/bnuoj/contest_show.php?cid=1605#problem/17964
 题目大意:
  鸣人发明了一种忍术,可以在短时间内提高查克拉。
  如果在某个时刻发动这个忍术,鸣人需要先消耗该时刻的查克拉值;
  在某个时候结束这个忍术,鸣人能获得该时刻的查克拉值(忍术必须先发动才能结束)
  如果某时刻鸣人具有的查克拉值少于该时刻的查克拉值,那么鸣人是不能发动该忍术的;
  鸣人知道了自己的查克拉的初始值,以及各个时刻的查克拉值,如果他最多可以发动两次
  该忍术(他也可以选择发动一次或者不发动),那么他最多能达到的查克拉值是多少?
 
  解题思路:
  
   我们令前一次发动忍术获得的查克拉为k,把只发动一次也看着两次(就当前一次获得的查克拉为零);
  这样我们就要维护k值,设当前时间为i,查克拉为A[i],前一段时间可以使用忍术需且需要消耗的查克拉
  最小值为Min,则如果当前时间结束忍术的话可以获得A[i]-Min的查克拉,这样我们就可以用A[i]-Min去更新k值
  得到1->i区间里如果发动一次忍术获得的查克拉的最大值k,如果当前可以发动第一次查克拉且当前的值比Min小,
  更新Min值;
  
   知道了求发动一次忍术可以获得的最大查克拉值,我们考虑构造发动第二次忍术,假设当前时间为i,且区间(1,i-1)
  里发动一次忍术获得的查克拉最大值为k(前面已经介绍过求法),且当前时间点可以再次发动,令m=k-A[i]表示第二次使用
  忍术后的剩下的查克拉,并维护m的最大值;

   设当前时间为i,使用两次忍术后得到的查克拉最大值为t,且区间(1,i-1)里面发动第二次忍术后剩下的查克拉最大值为m(上面介绍过),
  则当前点结束第二次忍术可以获得的查拉值为m+A[i],并更新t值。最后的t值就是答案。

 

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<algorithm>
using namespace std;
int A[11000];
int main()
{
    int i,c,n,Min,k,t,g,m;
    while(scanf("%d%d",&c,&n)!=EOF)
    {
		k=t=0;g=c;
		m=-1000000;
		Min=1000000;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
			t=max(t,m+A[i]);
            if(g>=A[i]){
                Min=min(Min,A[i]);
				m=max(m,k-A[i]);
            }
            k=max(k,A[i]-Min);
            g=max(g,k+c);
        }
        printf("%d\n",c+t);
    }
    return 0;
}


K题: 用邻接矩阵存储转移关系,因为一点到达另一点的概率是路劲上所有概率之积,这种情况和图论中走n步,一个点到另一个点的路劲条数的求法一样;

所以把一列之间的转移关系表示成一个邻接矩阵,这样就可以用解决图论中求路劲条数的方法来求了,也就是矩阵自乘n次,可以用矩阵快速幂发优化一下乘法,

代码如下:

#include<stdio.h>
#include<string.h>

int n;
struct mat{
	double M[30][30];
	mat(double v=0.0){
		for(int i=0;i<30;i++) 
			for(int j=0;j<30;j++)
				if(i==j) M[i][j]=v;else M[i][j]=0;
	}
	mat operator*(const mat &tem) const
	{
		int i,j,k; mat tmp(0);
		for(i=0;i<n;i++)
			for(j=0;j<n;j++)
				for(k=0;k<n;k++)
					tmp.M[i][j]+=M[i][k]*tem.M[k][j];
		return tmp;
	}
};
mat power(mat x,int y)
{
	mat sum(1);
	while(y){
		if(y&1) sum=sum*x;
		x=x*x;y>>=1;
	}return sum;
}

int main()
{
	int cas,m,k,s,i;
	scanf("%d",&cas);
	while(cas--){
		scanf("%d%d%d%d",&n,&m,&k,&s);
		mat ans(0); 
		if(n==1){ printf("1.0000\n"); continue; }
		for(i=0;i<n;i++){
			if(i>0) ans.M[i][i-1]=0.5;
			if(i<n-1) ans.M[i][i+1]=0.5;
		}
		ans.M[0][1]=ans.M[n-1][n-2]=1.0;
		ans=power(ans,m*k);
		for(i=0;i<n-1;i++)printf("%.4lf ",ans.M[s-1][i]);
		printf("%.4lf\n",ans.M[s-1][n-1]);
	}
	return 0;
}


 

H题:先解出两个交点,最后就是判断和那两条边交,并计算出一边的面积,最后输出最小面积就好了,代码写的很搓。。。。。

#include<stdio.h>
#include<string.h>
#include<string>
#include<math.h>
#include<map>
#include<set>
#include<algorithm>
#include<vector>
using namespace std;

int judge(double a,double b,double c,double x1,double y1,double x2,double y2,double &x,double &y)
{
    if(x1==x2){
        if(b){
             y=(a*x1+c)/(-b);
             x=x1;
             if(y>y2||y<y1) return 0;
             return 1;
        }
        else return 0;
    }
    else{
        if(a){
            x=(b*y1+c)/(-a);
            y=y1;
            if(x>x2||x<x1) return 0;
            return 1;
        }
        else return 0;
    }
}
struct node
{
    double x,y;
}num[10];

bool cmp(node a,node b)
{
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
int main()
{
    int cas;
    double x1,x2,y1,y2,a,b,c,ans,sum;
    scanf("%d",&cas);
    while(cas--){
        scanf("%lf%lf%lf%lf%lf%lf%lf",&x1,&y1,&x2,&y2,&a,&b,&c);
        sum=(x2-x1)*(y2-y1);
        if(judge(a,b,c,x1,y2,x2,y2,num[0].x,num[0].y)&&judge(a,b,c,x1,y1,x2,y1,num[1].x,num[1].y))
        {
            ans=(num[0].x+num[1].x-2*x1)*(y2-y1)/2;
        }
        else if(judge(a,b,c,x1,y1,x1,y2,num[0].x,num[0].y)&&judge(a,b,c,x2,y1,x2,y2,num[1].x,num[1].y))
        {
            ans=(num[0].y+num[1].y-y1*2)*(x2-x1)/2;
        }
        else if(judge(a,b,c,x1,y1,x1,y2,num[0].x,num[0].y)&&judge(a,b,c,x1,y2,x2,y2,num[1].x,num[1].y))
        {
            ans=(num[1].x-num[0].x)*(num[1].y-num[0].y)/2;
        }
        else if(judge(a,b,c,x1,y1,x1,y2,num[0].x,num[0].y)&&judge(a,b,c,x1,y1,x2,y1,num[1].x,num[1].y))
        {
            ans=(num[1].x-num[0].x)*(num[0].y-num[1].y)/2;
        }
        else if(judge(a,b,c,x1,y1,x2,y1,num[0].x,num[0].y)&&judge(a,b,c,x2,y1,x2,y2,num[1].x,num[1].y))
        {
            ans=(num[1].x-num[0].x)*(num[1].y-num[0].y)/2;
        }
        else if(judge(a,b,c,x2,y1,x2,y2,num[0].x,num[0].y)&&judge(a,b,c,x1,y2,x2,y2,num[1].x,num[1].y))
        {
            ans=(num[1].x-num[0].x)*(num[0].y-num[1].y)/2;
        }
        if(sum-ans<ans) ans=sum-ans;
        printf("%.3lf\n",ans);
    }
    return 0;
}


 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值