NOIP 2013 提高组题解 【附AC代码】

       NOIP2013很恶心地只得了365分,发挥不是很好,虽然在重庆这种弱省也可以拿一等奖了,但是重庆省选是要算NOIP成绩的,于是很伤心。但是再大的挫折也还是要走过去的,更何况省选还有机会,便有了这篇总结&&题解。

(代码见最后)

【Day1】


【T1】:

直接二分快速幂取模就过了。

时间复杂度O(logK)


【T2】:

求逆序对

NOIP 2013 提高组题解 【附AC代码】_第1张图片

然后,要原式最小的话,就要后面剪的那一坨最大。

很容易知道要那一坨最大的话,要小的数乘小的数,大的数乘大的数。(由排序不等式得)

那么算法就很简单了:

    1.对于数列a,b先离散化,将他们值替换为他们在所在数列的排名。

    2.算出a中每个数应该在b中的位置。

    3.对这个位置求一次逆序对。

时间复杂度O(N*logN)


【T3】

模型简化为:给出一个节点数为N,边数为M的无向带权图。Q次询问,每次询问两个点间路径中最小边权的最大值。

很容易能够想到,先对原图求一次最大生成树,得到一个森林。在这个森林中,任意两点间,要么不连通,要么有唯一路径。

而这个唯一路径中的最小边,就是最大的最小边。(这是显然的,因为我们是从大边开始选的。)

那么现在的问题就是对于每次询问,如何快速求出答案。

LCA!!

我们添加一个虚拟节点,这个节点对每个生成树连边,然后以这个点为root进行深搜遍历,得到一颗树。那么任意两点间的最小边权,用一个倍增的LCA就搞定了。

时间复杂度O(M*logM+Q*logN)


【Day2】


【T1】

很容易由贪心想到,对于每个联通块(这里联通块的含义是:最大的连续的一段积木,且中间没有高度为0的),找出最矮的一个。然后一次将这个联通块的高度全部减去这个高度,答案自然也加上这个高度。

但这样的复杂度是O(N^2)的,不能承受这么大的数据范围。

于是我们可以基于这个贪心原则,这样考虑:对于i,如果i的高度小于i-1的高度,那么我们在减去i-1的同时,一定可以减去i。

于是我们定义一个数组f[i],表示到i这个点花费了多少代价。

f[i]=f[i-1] (h[i]<=h[i-1])

而如果i的高度比i-1的高度大,那么我们在减去i-1之后,还必须额外付出(h[i]-h[i-1])的代价。

f[i]=f[i-1]+h[i]-h[i-1] (h[i]>h[i-1])

这样,f[N]就是最后的答案了。

(ps:其实拿一个单变量就可以完成了,不需要开数组)

时间复杂度O(N)


【T2】

题目简化为:求一个最大子序列的长度,这个子序列是波动的,即每个位置都是波峰或者波谷,且严格大于(小于)两边。

考试的时候没想到好办法,然后用了一个 二维DP+线段树优化 过了90‘,下来一想。我艹,这不是很简单的求波峰波谷么。一下子感觉考试的时候好秀逗。

算法就很简单了,我们从2到N-1扫一遍原序列,如果发现一个位置不大于两边的数,或者不小于两边的数,那么删除。最后剩的个数就是答案了。

为什么这样也很好理解,被删除的都是没有可能成为波峰或者波谷的,那么剩的一定就是最大的序列长度了。

时间复杂度O(N)


【T3】

看到这道题,第一反应是搜索。但是q大了的话,会超时。于是,我们可以想到去除搜索中的冗余信息。
现在来分析题目性质:
(这里称要移动的棋子为目标棋子)
    首先,如果要移动目标棋子,那么我们首先必须要将空格移到该棋子的上下左右四个方向上相邻位置之一,然后才可以移动该棋子。
    然后,我们分析该棋子移动时候的性质:
        棋子每次可以移动,当且仅当空格位于其相邻位置的时候,每次移动完棋子,空格总会在棋子相邻的位置,那么我们发现,对于棋子在某一位置,然后空格又在其四个方向上某一相邻位置时,棋子要想某一方向移动一个时的花费的步数是一定的
    那么,就可以先进行一次预处理,预处理出对于目标棋子在上述条件下每次移动所需的步数。
    定义一个数组Move[x][y][k][h],表示目标棋子在位置(x,y)且空格在它的k方向的相邻格子,目标棋子往h方向移动1格所需要的步数。
    然后对于状态[x][y][k],对它编号后,为节点建图,用状态转移方式连边。
    每次询问的时候,重新定义一个起点和终点,编号连边,跑一次最短路就可以得出答案。

时间复杂度O(N^4+q*N^2) (SPFA)


【代码】


【D1 - T1】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
long long N,M,K,X;
void _init()
{
	cin>>N>>M>>K>>X;
}
long long _efksm(long long a,long long b,long long c)
{
	long long ans=1;
	a%=c;
	while(b)
	{
		if(b&1) ans=(ans*a)%c;
		b>>=1;
		a=(a*a)%c;
	}
	return ans;
}
void _solve()
{
	long long temp=_efksm(10LL,K,N);
	M%=N;
	temp=(temp*M)%N;
	X%=N;
	temp=(temp+X)%N;
	cout<



【D1 - T2】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MODER=99999997;
const int MAXN=100005;
struct node{int v,rank,loc;};
node a[MAXN],b[MAXN];
int temp[MAXN],loc[MAXN];
int N;
long long ans=0;
bool _cmpv(const node A,const node B){return A.v>1;
	_mergesort(l,mid);
	_mergesort(mid+1,r);
	int i=l,j=mid+1,pos=0;
	while(i<=mid&&j<=r)
	{
		if(a[i].rank

【D1 - T3】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXM=50005;
const int MAXQ=30005;
const int MAXN=10005;
const int INF=999999999;
struct edge{
	int x,y,z;
}Edge[MAXM];
int N,M,Q,tot,s;
int father[MAXN];
int yt[MAXM<<1],z[MAXM<<1],last[MAXN],next[MAXM<<1];
int f[MAXN][20],Min[MAXN][20],dep[MAXN];
bool vis[MAXN];
int _getfather(int x)
{
	return (x==father[x])?x:father[x]=_getfather(father[x]);
}
bool _cmpz(const edge A,const edge B){return A.z>B.z;}
void _read(int &x)
{
	char tt=getchar();
	while(tt<'0'||'9'>i)&1)
		{
			ans=min(ans,Min[x][i]);
			x=f[x][i];
		}
	if(x==y) return ans;
	int t=ceil(log2(dep[x]));
	for(int i=t;i>=0;i--)
		if(f[x][i]!=f[y][i])
		{
			ans=min(ans,min(Min[x][i],Min[y][i]));
			x=f[x][i];
			y=f[y][i];
		}
	if(f[x][0]==N+1)
		return INF;
	return min(ans,min(Min[x][0],Min[y][0]));
}
void _solve()
{
	sort(Edge+1,Edge+M+1,_cmpz);
	int k,cnt,fx,fy;
	k=cnt=0;
	//求出最大生成森林
	while(true)
	{
		k++;
		fx=_getfather(Edge[k].x);
		fy=_getfather(Edge[k].y);
		if(fx==fy&&fx==0)
		    break;
		if(fx!=fy)
		{
			father[fx]=fy;
			tot++;
			yt[tot]=Edge[k].y;z[tot]=Edge[k].z;
			next[tot]=last[Edge[k].x];
			last[Edge[k].x]=tot;
			tot++;
			yt[tot]=Edge[k].x;z[tot]=Edge[k].z;
			next[tot]=last[Edge[k].y];
			last[Edge[k].y]=tot;
		}
	}
	//虚拟节点连边
	for(int i=1;i<=N;i++)
		if(father[i]==i)
		{
			tot++;
			yt[tot]=i;z[tot]=INF;
			next[tot]=last[N+1];
			last[N+1]=tot;
		}
	//
	k=ceil(log2(N));
	for(int i=1;i<=N;i++)
		for(int j=0;j<=k;j++)
			Min[i][j]=INF;
	f[N+1][0]=N+1;Min[N+1][0]=INF;
	_DFS(N+1,1);
	_read(Q);
	int a,b;
	s=ceil(log2(N));
	//LCA在线处理询问
	for(int i=1;i<=Q;i++)
	{
		_read(a);_read(b);
		int ans=_LCA(a,b);
		printf("%d\n",(ans==INF)?-1:ans);
	}
}
int main()
{
	_init();
	_solve();
	return 0;
}

【D2 - T1】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=100005;
int N,h[MAXN];
void _init()
{
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
		scanf("%d",&h[i]);
}
void _solve()
{
	int ans=0;
	for(int i=1;i<=N;i++)
		if(h[i-1]

【D2 - T2】

#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN=100005;
int N;
int V[MAXN],Left[MAXN],Right[MAXN];
void _read(int &x)
{
	char tt=getchar();
	while(tt<'0'||'9'=V[i]&&V[i]>=V[Right[i]])||(V[Left[i]]<=V[i]&&V[i]<=V[Right[i]]))
		{
			ans--;
			Right[Left[i]]=Right[i];
			Left[Right[i]]=Left[i];
		}
	cout<

【D2 - T3】

/*
    ID:Ciocio
	LANG:C++
	DATE:2013-11-19
	TASK:Puzzle
*/
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

#define MAXN 35
#define INF 999999999
#define MAXV 60000

int d[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
int Move[MAXN][MAXN][4][4],Map[MAXN][MAXN];
int dist[MAXN][MAXN],v[MAXN][MAXN][4];
int N,M,q,V;
int y[MAXV],z[MAXV],next[MAXV],last[MAXV];

void _init()
{
	scanf("%d%d%d",&N,&M,&q);
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
		{
			scanf("%d",&Map[i][j]);
			for(int k=0;k<4;k++)
				v[i][j][k]=++V;
		}
}

bool _overflow(int i,int j){return (!(1<=i&&i<=N&&1<=j&&j<=M));}

struct node{
	int x,y;
	node (int _x,int _y):x(_x),y(_y){}
};
queueQ;

int _BFS(int Sx,int Sy,int Tx,int Ty)
{
	if(Sx==Tx&&Sy==Ty) return 0;
	while(!Q.empty()) Q.pop();
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			dist[i][j]=INF;
	dist[Sx][Sy]=0;
	Q.push(node(Sx,Sy));
	while(!Q.empty())
	{
		node t=Q.front();Q.pop();
		for(int k=0;k<4;k++)
			if(!_overflow(t.x+d[k][0],t.y+d[k][1]))
				if(Map[t.x+d[k][0]][t.y+d[k][1]]&&dist[t.x+d[k][0]][t.y+d[k][1]]==INF)
				{
					dist[t.x+d[k][0]][t.y+d[k][1]]=dist[t.x][t.y]+1;
					if(t.x+d[k][0]==Tx&&t.y+d[k][1]==Ty)
						return dist[t.x][t.y]+1;
					Q.push(node(t.x+d[k][0],t.y+d[k][1]));
				}
	}
	return INF;
}

int tot=0;

void _addedge(int a,int b,int c)
{
	tot++;
	y[tot]=b;z[tot]=c;
	next[tot]=last[a];
	last[a]=tot;
}

void _pretreat()
{
	
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			for(int k=0;k<4;k++)
				for(int h=0;h<4;h++)
					Move[i][j][k][h]=INF;
					
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			if(Map[i][j])
			{
				Map[i][j]=0;
				for(int k=0;k<4;k++)
					if((!_overflow(i+d[k][0],j+d[k][1]))&&Map[i+d[k][0]][j+d[k][1]])
						for(int h=0;h<4;h++)
							if(!_overflow(i+d[h][0],j+d[h][1]))
								if(Map[i+d[h][0]][j+d[h][1]])
									Move[i][j][k][h]=_BFS(i+d[k][0],j+d[k][1],i+d[h][0],j+d[h][1])+1;
				Map[i][j]=1;
			}
			
	for(int i=1;i<=N;i++)
		for(int j=1;j<=M;j++)
			for(int k=0;k<4;k++)
				for(int h=0;h<4;h++)
					if(Move[i][j][k][h]Dist[y];}
};
priority_queue,Cmp>PQ;

int _SPFA(int Sta,int End)
{
	while(!PQ.empty()) PQ.pop();
	for(int i=1;i<=V;i++)
		Dist[i]=INF;
	memset(f,0,sizeof(f));
	Dist[Sta]=0;
	f[Sta]=true;
	PQ.push(Sta);
	while(!PQ.empty())
	{
		int t=PQ.top();PQ.pop();
		f[t]=false;
		for(int j=last[t];j;j=next[j])
			if(Dist[y[j]]>Dist[t]+z[j])
			{
				Dist[y[j]]=Dist[t]+z[j];
				if(!f[y[j]])
				{
				    f[y[j]]=true;
				    PQ.push(y[j]);
				}
			}
	}
	return (Dist[End]


你可能感兴趣的:(算法分析)