NOIP2013题解

NOIP2013题解

Day1

转圈游戏 circle

快速幂模板题。

#include<iostream>
using namespace std;
int n,m,k,x;
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%n;a=1ll*a*a%n;b>>=1;}return s;}
int main()
{
	cin>>n>>m>>k>>x;
	m=1ll*m*fpow(10,k)%n;
	cout<<(x+m)%n<<endl;
	return 0;
}

火柴排队 match

比较不错的题目。

不难发现显然上下的排名一样的时候是最优解,对于上方重编号,转为逆序对问题。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100100
#define MOD 99999997
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
struct Node{int a,b;}p[MAX];
int n,S[MAX],top,a[MAX],ans;
int c[MAX];
int lb(int x){return x&(-x);}
void add(int x,int w){while(x<=n)c[x]+=w,x+=lb(x);}
int getsum(int x){int ret=0;while(x)ret+=c[x],x-=lb(x);return ret;}
int main()
{
	n=read();
	for(int i=1;i<=n;++i)p[i].a=read();
	for(int i=1;i<=n;++i)p[i].b=read();
	top=0;
	for(int i=1;i<=n;++i)S[++top]=p[i].a;
	sort(&S[1],&S[n+1]);top=unique(&S[1],&S[top+1])-S-1;
	for(int i=1;i<=n;++i)p[i].a=lower_bound(&S[1],&S[top+1],p[i].a)-S;
	top=0;
	for(int i=1;i<=n;++i)S[++top]=p[i].b;
	sort(&S[1],&S[n+1]);top=unique(&S[1],&S[top+1])-S-1;
	for(int i=1;i<=n;++i)p[i].b=lower_bound(&S[1],&S[top+1],p[i].b)-S;
	for(int i=1;i<=n;++i)a[p[i].a]=i;
	for(int i=1;i<=n;++i)p[i].b=a[p[i].b];
	for(int i=1;i<=n;++i)a[i]=p[i].b;
	for(int i=n;i;--i)(ans+=getsum(a[i]))%=MOD,add(a[i],1);
	printf("%d\n",ans);
	return 0;
}

货车运输 truck

不错的题目,做过一次你就会做了。

不难发现答案一定在最大生成树上,所以构建最大生成树之后,答案就是两点间的路径最大值了。

可以倍增,可以树链剖分+线段树。也可以直接克鲁斯卡尔重构树。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 20200
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,m;
struct Edge{int u,v,w;}E[MAX*5];
bool operator<(Edge a,Edge b){return a.w>b.w;}
int f[MAX],tot,W[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
struct Line{int v,next;}e[MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int p[16][MAX],dep[MAX];
void dfs(int u,int ff)
{
	p[0][u]=ff;dep[u]=dep[ff]+1;
	for(int i=1;i<16;++i)p[i][u]=p[i-1][p[i-1][u]];
	for(int i=h[u];i;i=e[i].next)
		dfs(e[i].v,u);
}
int LCA(int u,int v)
{
	if(dep[u]<dep[v])swap(u,v);
	for(int i=15;~i;--i)
		if(dep[p[i][u]]>=dep[v])u=p[i][u];
	if(u==v)return u;
	for(int i=15;~i;--i)
		if(p[i][u]^p[i][v])
			u=p[i][u],v=p[i][v];
	return p[0][u];
}
int main()
{
	n=read();m=read();tot=n;
	for(int i=1;i<=m;++i)E[i].u=read(),E[i].v=read(),E[i].w=read();
	sort(&E[1],&E[m+1]);
	for(int i=1;i<n+n;++i)f[i]=i;
	for(int i=1;i<=m;++i)
	{
		int u=getf(E[i].u),v=getf(E[i].v);
		if(u==v)continue;
		f[u]=f[v]=++tot;W[tot]=E[i].w;
		Add(tot,u);Add(tot,v);
	}
	for(int i=tot;i;--i)
		if(!dep[i])dfs(i,0);
	int Q=read();
	while(Q--)
	{
		int u=read(),v=read();
		if(getf(u)!=getf(v))puts("-1");
		else printf("%d\n",W[LCA(u,v)]);
	}
	return 0;
}

Day2

积木大赛 block

略有思维的题目。

我们从第一个开始,显然无论如何我们都要以第一个为左端点执行第一个的高度次操作。那么我们不限定右端点,让这个操作的区间自然向右延伸。而每个积木的高度则限定了可以向右延伸的次数,那么直接算一遍就好了。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,h[100100];
ll ans=0,nw=0;
int main()
{
	n=read();
	for(int i=1;i<=n;++i)h[i]=read();
	for(int i=1;i<=n;nw=h[i++])
		if(h[i]>=nw)ans+=h[i]-nw;
	printf("%lld\n",ans);
	return 0;
}

花匠 flower

发现要求的就是一个最长波动序列。考虑一种\(dp\)做法,设\(f[i][0/1]\)表示当前第\(i\)个位置,它是峰开始谷,转移的时候分类讨论一下。如果\(h_i>h_{i+1}\),那么\(f[i][1]=f[i-1][0]+1\),因为可以把当前位置当做峰。然后\(f[i][1]=f[i-1][1]\),因为当前位置代替一个更低的位置当做峰一定更优。反过来的转移类似。

有了这个\(dp\)不难发现贪心就行了。

#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int ans=1,n,h[100100],nw=-1;
int main()
{
	n=read();
	for(int i=1;i<=n;++i)h[i]=read();
	for(int i=2;i<=n;++i)
	{
		if(h[i]>h[i-1]&&nw!=1)++ans,nw=1;
		if(h[i]<h[i-1]&&nw!=0)++ans,nw=0;
	}
	printf("%d\n",ans);
	return 0;
}

华容道 puzzle

可以说是\(NOIP\)中最优秀的题目之一。

然而做过一次就再也忘不了了。所以这回一下就写完了。

发现如果空格如果在当前的棋子周围,那么问题转化为了空格从当前棋子旁边的一个方向移动到另外一个方向上的问题。预处理\(dis[x][y][0..4][0..4]\)表示当前位置\((x,y)\),空格在某个方向上,空格要移动到另外一个方向上、在不经过\((x,y)\)位置的情况下的最小步数。只需要对于每个点\(bfs\)一次就好了。

然后对于每次询问,先让空格到达起点旁边,再直接跑一遍最短路就好了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define MAX 35
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int d[4][2]={1,0,-1,0,0,1,0,-1};
bool vis[MAX][MAX];
int dis[MAX][MAX];
int n,m,Q,a[MAX][MAX];
int mov[MAX][MAX][4][4];
void bfs(int x,int y,int X,int Y)
{
	queue<int> Qx,Qy;Qx.push(x),Qy.push(y);
	memset(dis,63,sizeof(dis));dis[x][y]=0;
	memset(vis,0,sizeof(vis));vis[x][y]=vis[X][Y]=true;
	while(!Qx.empty())
	{
		int x=Qx.front(),y=Qy.front();Qx.pop();Qy.pop();
		for(int i=0;i<4;++i)
		{
			int xx=x+d[i][0],yy=y+d[i][1];
			if(xx<1||yy<1||xx>n||yy>m||a[xx][yy]==0)continue;
			if(vis[xx][yy])continue;vis[xx][yy]=true;
			Qx.push(xx);Qy.push(yy);
			dis[xx][yy]=dis[x][y]+1;
		}
	}
}
int Dis[MAX][MAX][4];
bool Vis[MAX][MAX][4];
int SPFA(int Ex,int Ey,int Sx,int Sy,int Tx,int Ty)
{
	if(Sx==Tx&&Sy==Ty)return 0;
	int ans=2e9;bfs(Ex,Ey,Sx,Sy);
	memset(Dis,63,sizeof(Dis));
	queue<int> Qx,Qy,Qd;
	for(int i=0;i<4;++i)
	{
		int x=Sx+d[i][0],y=Sy+d[i][1];
		if(x<1||y<1||x>n||y>m||!a[x][y])continue;
		if(dis[x][y]>1e9)continue;
		Dis[Sx][Sy][i]=dis[x][y];Vis[Sx][Sy][i]=true;
		Qx.push(Sx);Qy.push(Sy);Qd.push(i);
	}
	while(!Qx.empty())
	{
		int x=Qx.front(),y=Qy.front(),D=Qd.front();
		Qx.pop();Qy.pop();Qd.pop();
		for(int i=0;i<4;++i)
		{
			int xx=x+d[i][0],yy=y+d[i][1];
			if(xx<1||yy<1||xx>n||yy>m||!a[xx][yy])continue;
			int w=mov[x][y][D][i]+Dis[x][y][D]+1;
			if(Dis[xx][yy][i^1]>w)
			{
				Dis[xx][yy][i^1]=w;
				if(!Vis[xx][yy][i^1])
					Vis[xx][yy][i^1]=true,Qx.push(xx),Qy.push(yy),Qd.push(i^1);
			}
		}
		Vis[x][y][D]=false;
	}
	for(int i=0;i<4;++i)ans=min(ans,Dis[Tx][Ty][i]);
	if(ans>1e9)ans=-1;
	return ans;
}
int main()
{
	n=read();m=read();Q=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			a[i][j]=read();
	memset(mov,63,sizeof(mov));
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			if(a[i][j])
				for(int k=0;k<4;++k)
				{
					int x=i+d[k][0],y=j+d[k][1];
					if(x<1||y<1||x>n||y>m)continue;
					if(!a[x][y])continue;
					bfs(x,y,i,j);
					for(int l=0;l<4;++l)
					{
						int xx=i+d[l][0],yy=j+d[l][1];
						mov[i][j][k][l]=dis[xx][yy];
					}
				}
	while(Q--)
	{
		int Ex=read(),Ey=read(),Sx=read(),Sy=read(),Tx=read(),Ty=read();
		printf("%d\n",SPFA(Ex,Ey,Sx,Sy,Tx,Ty));
	}
	return 0;
}
posted @ 2018-11-08 15:53  小蒟蒻yyb  阅读(386)  评论(0编辑  收藏  举报