• 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
    ",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
    ",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
    ",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
    ",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
    ",SPFA(Ex,Ey,Sx,Sy,Tx,Ty));
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java生成json
    WinForm程序执行JS代码的多种方法以及使用WebBrowser与JS交互
    聚集索引和非聚集索引的区别
    如何编写函数才能提高代码质量
    前端程序员应该知道的15个 jQuery 小技巧
    FileShare枚举的使用(文件读写锁)
    ASP.NET MVC 数据库依赖缓存的实现
    C# 调用一个按钮的Click事件(利用反射)
    解决报错“超时时间已到。超时时间已到,但是尚未从池中获取连接”的方案
    关于浏览器URL中出现会话验证字符说明
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9929541.html
Copyright © 2020-2023  润新知