NOIP2014提高组解析

题目见此


day1:

生活大爆炸:

裸裸模拟。我是打一个01(胜负)的表,我觉得一一if比较麻烦。


参考程序:

#include<cstdio>
#include<algorithm>
#define maxn 300
using namespace std;
int a[maxn],b[maxn];
int c[5][5]={
	{0,0,1,1,0},
	{1,0,0,1,0},
	{0,1,0,0,1},
	{0,0,1,0,1},
	{1,1,0,0,0},
};
int main(){
	freopen("rps.in","r",stdin);
	freopen("rps.out","w",stdout);
	int na,nb,n;
	scanf("%d%d%d",&n,&na,&nb);
	for (int i=0;i<na;i++)
		scanf("%d",&a[i]);
	for (int i=0;i<nb;i++)
		scanf("%d",&b[i]);
	int i=0,j=0;
	int resa=0,resb=0;
	for (int k=0;k<n;k++){
		resa+=c[a[i]][b[j]];
		resb+=c[b[j]][a[i]];
		i=(i+1)%na;j=(j+1)%nb;
	}
	printf("%d %d",resa,resb);
	return 0;
}


联合权值:

裸裸的模拟,这是一个假“图论”,(提示:距离为2表示与该点距离为1的点距离为1不包括该点的点。。。比较绕)

因此为了简化问题,我们可以设sum[i]表示直接与点i相连的点的权值之和,ans[i]表示直接与点i相连的点的权值的最大值,pos[i]表示直接与点i相连的权值最大的那个点,sec[i]表示次大值,根据题目这些都在longlong范围内,无需高精度。预处理O(m)

那么,令v为所有与u距离为1的点,所有与点u距离为2的点j的权值和为w[u]*(sum[v]-w[u]),

而,所有j的权值的最大值就需要讨论,如果pos[v]=u则为sec[v],反之就是ans[v].

计算时就可以取模,还是无需高精度,效率O(m)。(无需建图,直接枚举边)


参考程序:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 410000
#define LL long long
using namespace std;
int n;
int max1[maxn],max2[maxn];
int pos[maxn];
LL sum[maxn];
int u[maxn],v[maxn],w[maxn];
int main(){
	freopen("link.in","r",stdin);
	freopen("link.out","w",stdout);
	scanf("%d",&n);
	for (int i=0;i<n-1;i++){
		scanf("%d%d",&u[i],&v[i]);
		u[n+i-1]=v[i];v[n+i-1]=u[i];
	}
    for (int i=1;i<=n;i++)
		scanf("%d",&w[i]);
	memset(max1,0,sizeof(max1));
	memset(max2,0,sizeof(max2));
	memset(sum,0,sizeof(sum));
	for (int i=0;i<2*n-2;i++){
		if (w[v[i]]>max1[u[i]]){
			max2[u[i]]=max1[u[i]];
			max1[u[i]]=w[v[i]];
			pos[u[i]]=v[i];
		}else if (w[v[i]]>max2[u[i]]){
			max2[u[i]]=w[v[i]];
		}
		sum[u[i]]+=w[v[i]];
	}
	LL ans=0;
	int res=0;
	for (int i=0;i<2*n-2;i++){
		if(pos[v[i]]==u[i])
			res=max(res,max2[v[i]]*w[u[i]]);
		else res=max(res,max1[v[i]]*w[u[i]]);
		ans+=(sum[v[i]]-w[u[i]])*w[u[i]];
		ans%=10007;
	}
	printf("%d %lld",res,ans);
	return 0;
}


飞扬的小鸟:

稍稍麻烦一点的裸无限背包。


参考程序:

#include<cstdio>
#include<algorithm>
#define INF 0x3f3f3f3f
#define maxn 11000
using namespace std;
int n,m,p;
int x[maxn],y[maxn];
int l[maxn],h[maxn];
int f[maxn][1100];
bool mark[maxn];
int main(){
	freopen("bird.in","r",stdin);
	freopen("bird.out","w",stdout);
	scanf("%d%d%d",&n,&m,&p);
	for (int i=1;i<=n;i++)
		scanf("%d%d",&x[i],&y[i]);
	memset(l,0,sizeof(l));
	memset(h,0x3f,sizeof(h));
	for (int i=1;i<=p;i++){
		int no;
		scanf("%d",&no);
		scanf("%d%d",&l[no],&h[no]);
		mark[no]=true;
	}
	memset(f,0x3f,sizeof(f));
	for (int i=0;i<=m;i++)
		f[0][i]=0;
	//for (int i=1;i<=n;i++)
	//	printf("%d %d %d\n",mark[i],l[i],h[i]);
	int res=0;
	for (int i=1;i<=n;i++){
		for (int j=x[i]+1;j<=m;j++)
			f[i][j]=min(f[i][j],min(f[i-1][j-x[i]],f[i][j-x[i]])+1);
		for (int j=m-x[i];j<=m;j++)
			f[i][m]=min(f[i][m],min(f[i-1][j],f[i][j])+1);
		for (int j=1;j<=m-y[i];j++)
			f[i][j]=min(f[i][j],f[i-1][j+y[i]]);
		if (mark[i]){
		    for (int j=0;j<=l[i];j++)
			    f[i][j]=INF;
		    for (int j=h[i];j<=m;j++)
			    f[i][j]=INF;
		}
		bool flag=false;
		for (int j=1;j<=m;j++)
			if (f[i][j]!=INF)flag=true;
		if (flag)res+=mark[i];
	    else{
	       printf("0\n%d\n",res);
		   return 0;
	    }		
	}	
	int ans=INF;
	for (int i=1;i<=m;i++)
	   ans=min(ans,f[n][i]);
	/*for (int j=0;j<=m;j++){
	    for (int i=0;i<=n;i++)
			printf("%5d ",f[i][j]==INF?-1:f[i][j]);
		printf("\n");
	}*/
	printf("1\n%d\n",ans);
	return 0;
}


day2:

无线网:

两层循环模拟,设f[i][j]表示从[0,0]到[i,j]的矩阵中公共场所的总数,预处理O(n^2),然后枚举中心点,向左右扩展用f[i][j]O(1)的速度算出打擂台即可。


参考程序:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=500;
int a[maxn][maxn];
int f[maxn][maxn];
int d,n;
int main(){
	freopen("wireless.in","r",stdin);
	freopen("wireless.out","w",stdout);
	scanf("%d%d",&d,&n);
	for (int i=0;i<n;i++){
		int x,y,k;
		scanf("%d%d%d",&x,&y,&k);
		x++;y++;
		a[x][y]=k;
	}
	for (int i=1;i<=129;i++)
		for (int j=1;j<=129;j++)
			f[i][j]=max(f[i][j],f[i-1][j]+f[i][j-1]-f[i-1][j-1]+a[i][j]);
	int cnt=0,ans=0;
	for (int i=1;i<=129;i++)
		for (int j=1;j<=129;j++){
			int x1=max(i-d,1),x2=min(i+d,129),y1=max(j-d,1),y2=min(j+d,129);
			int z=f[x2][y2]-f[x1-1][y2]-f[x2][y1-1]+f[x1-1][y1-1];
			if (z>ans){
				ans=z;cnt=1;
			}else 
			if (z==ans)cnt++;
		}
	printf("%d %d",cnt,ans);
	return 0;
}



寻找道路:

这是真的图,建两个图,一个是顺向,一个是反向,先用反向图dfs出能到t的点,再在顺向图中筛出符合条件一的点,最后spfa即可。


参考程序:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<ctime>
#define maxn 210000
using namespace std;
struct Node{
	int j,next;
}e[maxn],E[maxn];
int a[maxn],A[maxn],dis[maxn];
int n,m,s,t,NodeCnt=0;
bool ok[maxn],can[maxn],inq[maxn],vis[maxn];
queue<int> Q;
void addedge(int u,int v){
	int p=++NodeCnt;
	e[p].j=v;e[p].next=a[u];
	a[u]=p;
	E[p].j=u;E[p].next=A[v];
	A[v]=p;
}
void dfs(int x){
	if (can[x])return;
	can[x]=true;
	for (int p=A[x];p;p=E[p].next){
		int j=E[p].j;
		dfs(j);
	}
}
int spfa(int s){
	memset(dis,0x3f,sizeof(dis));
	memset(inq,0,sizeof(inq));
	int INF=dis[0];
	Q.push(s);
	dis[s]=0;inq[s]=true;
	while (!Q.empty()){
		int u=Q.front();inq[u]=false;Q.pop();
		for (int p=a[u];p;p=e[p].next){
			int j=e[p].j;
			if (ok[j] && dis[j]>dis[u]+1){
				dis[j]=dis[u]+1;
				if (!inq[j]){
					Q.push(j);
					inq[j]=true;
				}
			}
		}
	}
	return dis[t]==INF?-1:dis[t];
}
int main(){
	freopen("road.in","r",stdin);
	freopen("road.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=0;i<m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		if (u==v)continue;
		addedge(u,v);
	}
	scanf("%d%d",&s,&t);
	dfs(t);
	for (int i=1;i<=n;i++){
		ok[i]=true;
		for (int p=a[i];p;p=e[p].next){
			int j=e[p].j;
			ok[i]&=can[j];
		}
	}
	printf("%d",spfa(s));
	return 0;
}


解方程:

这一题比较坑,神犇一眼看出用秦九韶算法,可是,

我们就模质数吧,如果模了后的左式为0,可以粗略的认为这个就是根,多模几个质数即可,然后预处理出(1,p)(p指该质数)左式的值,可知f(x)=f(x+p)(mod p),根据一个根即可得出其他的根。


参考程序:

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 11000
using namespace std;
int pri[5]={11261,19997,22877,21893,14843};
char Ai[maxn];
int a[5][maxn];
int pre[5][maxn];
int ans[100*maxn];
int res[5][3*maxn];
int n,m;
int cal(int k,int x){
	int sum=0;
	for (int i=0;i<=n;i++)
		sum=(sum+a[k][i]*pre[k][i])%pri[k];
	if (sum<0)sum+=pri[k];
	return sum;
}
int check(int x){
	for (int k=0;k<5;k++)
		if (res[k][x%pri[k]])return false;
	return true;
}
int main(){
	freopen("equation.in","r",stdin);
	freopen("equation.out","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=0;i<=n;i++){
		scanf("%s",Ai);
		int lt=strlen(Ai);
		bool flag=0;
		for (int k=0;k<5;k++)
			if (Ai[0]!='-')a[k][i]=Ai[0]-'0';
				else a[k][i]=0,flag=true;
		for (int k=0;k<5;k++){
		    for (int j=1;j<lt;j++)
				a[k][i]=(a[k][i]*10+Ai[j]-48)%pri[k];
			if (flag)a[k][i]=-a[k][i];
		}
	}
	for (int k=0;k<5;k++)
		for (int x=1;x<pri[k];x++){
			pre[k][0]=1;
			for (int i=1;i<=n;i++)
				pre[k][i]=(pre[k][i-1]*x)%pri[k];
			res[k][x]=cal(k,x);
		}
	int cnt=0;
	for (int i=1;i<=m;i++)
		if (check(i))ans[++cnt]=i;
	printf("%d\n",cnt);
	for (int i=1;i<=cnt;i++)
		printf("%d\n",ans[i]);
	return 0;
}


给神犇解读的秦九韶:

var pr:array[0..70]of int64;
    b:array[0..1000010]of boolean;
    a:array[0..10010]of longint;
    c:array[0..110,0..70]of int64;
    fh:array[0..110]of longint;
    ys:array[0..6000]of boolean;
    n,m,i,j,k,s,ans,l:longint;
    ok:boolean;
    num,q:int64;
    ch:char;
begin
   assign(input,'equation.in');reset(input);
   assign(output,'equation.out');rewrite(output);
   readln(n,m);
   s:=1;
   pr[s]:=5683;
   num:=int64(200000)*200000;
   repeat
      num:=num+1;ok:=true;
      for i:=2 to 200000 do
       if num mod i=0 then
        begin
          ok:=false;
          break;
        end;
      if ok then begin s:=s+1;pr[s]:=num;end;
   until s=10;
   for i:=0 to n do
      begin
         repeat
           read(ch);
         until ch>' ';
         if ch='-'then
           begin
             fh[i]:=-1;
             read(ch);
           end
         else fh[i]:=1;
         l:=1;
         q:=1;
         fillchar(a,sizeof(a),0);
         repeat
            if q=1000000 then
              begin
                l:=l+1;
                q:=1;
              end;
            a[l]:=a[l]*10+ord(ch)-48;
            q:=q*10;
            if seekeoln(input)then break;
            read(ch);
         until false;
         for j:=1 to s do
            begin
               num:=0;
               for k:=1 to l-1 do num:=(num*1000000+a[k])mod pr[j];
               num:=(num*q+a[l])mod int64(pr[j]);
               c[i,j]:=num*fh[i];
            end;
      end;
   for i:=1 to m do
      begin
         if(i>5683)and(ys[(i-1)mod 5683+1]=false)then continue;
         b[i]:=true;
         for j:=1 to s do
          if b[i] then
            begin
               num:=0;
               for k:=n downto 0 do num:=(num*i+c[k,j])mod pr[j];
               if(i<=5683)and(j=1)then ys[i]:=(num=0);
               if num<>0 then b[i]:=false;
            end;
      end;
   for i:=1 to m do
    if b[i] then ans:=ans+1;
   writeln(ans);for i:=1 to m do if b[i]then writeln(i);
   close(input);close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值