【解题报告】NOIP2013

【解题报告】NOIP2013

Day1

T1 转圈游戏

思路

我们手模发现,就是 \(x+m \times 10^k \space mod \space n\)

然后一个快速幂就没了,开 long long

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
int n,m,k,x;
long long ksm(long long a,long long b,int p)
{
	long long ans=1%p;
	for( ;b;b>>=1)
	{
		if(b&1)
		ans=(long long)ans*a%p;
		a=(long long)a*a%p; 
	}
	return ans;
}
int main()
{
	cin>>n>>m>>k>>x;
	cout<<(ksm(10,k,n)*m%n+x%n)%n<<endl;
	return 0;
}

T2 火柴排队

思路

值域树状数组

我们要使得 \(\sum (a_i-b_i)^2\) 最小

变形一下

\[\sum (a_i^2+2a_ib_i+b_i^2) \\= \sum (a_i^2+b_i^2)+\sum2a_ib_i \]

我们发现 \(\sum (a_i^2+b_i^2)\) 是一个定值,所以我们只需要让 \(\sum 2a_ib_i\) 最小就好了

如何让两个数列的 \(2a_ib_i\) 最小呢

我们可以让两个序列中的数字各自标上排名

然后如果两个数列的数字的排名相对应,我们就可以让它最小

比如说

2 3 1 4

3 2 1 4

我们只需要交换一次

再比如

1 3 4 2

1 7 2 4

我们让他们对应,也就是将第二个序列中的 7 2 4 变成 4 7 2 ,交换两次

那我们就以第一个数列为基准,对于这个数列我们就可以离散化一下,表示排名,然后建立两个数列间的类似于最长公共子序列的 \(O(n^2)\) 的做法的方法来建立一一映射的关系

值域树状数组求逆序对即可

这样就做完了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
const int maxn=500005;
const int mod=99999997;
int tree[maxn],rank[maxn],n;
long long ans;
struct point{
	int num,val;
}a[maxn],b[maxn];
int lowbit(int x)
{
	return x&(-x);
}
bool cmp(point q,point w)
{
	if(q.val==w.val)
	return q.num<w.num;
	return q.val<w.val;
}
void add(int x,int y)
{
	for( ;x<=n;x+=lowbit(x)) tree[x]+=y,tree[x]%=mod;
}
int ask(int x)
{
	int ans=0;
	for( ;x;x-=lowbit(x)) ans+=tree[x],ans%=mod;
	return ans;
}
int main()
{
	std::ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i].val;
		a[i].num=i;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>b[i].val;
		b[i].num=i;
	}
	sort(a+1,a+1+n,cmp);
	sort(b+1,b+1+n,cmp);
	for(int i=1;i<=n;i++)
	rank[a[i].num]=b[i].num;
	for(int i=1;i<=n;i++)
	{
		add(rank[i],1);
		ans+=(i-ask(rank[i]));
		ans%=mod;
	}
	cout<<ans<<endl;
	return 0;
}

T3 货车运输

思路

最大生成树 + LCA

我们要求的是每一条路径中最小边的最大值,这个最大值肯定会加入最大生成树,所以我们做一个最大生成树

然后,对于这棵树,我们倍增预处理每一条边到自己的 \(k\) 级祖先的答案,然后我们用倍增 \(lca\) 来合并答案

然后就没了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath> 
using namespace std; 

const int maxn=10005;
const int INF=999999999;

struct edge1{  
    int x,y,dis;
}ed1[50005]; 
struct edge2{
    int to,next,w;
}ed2[100005];
int cnt,n,m,head[maxn],deep[maxn],f[maxn],fa[maxn][21],w[maxn][21];
bool vis[maxn]; 
void add_edge(int from, int to, int w)
{
    ed2[++cnt].next=head[from];
    ed2[cnt].to=to;
    ed2[cnt].w=w;
    head[from]=cnt;
    return ;
}

bool cmp(edge1 x, edge1 y)
{
    return x.dis>y.dis; //将边权从大到小排序 
}

int find(int x)
{
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}

void kruskal()
{
    sort(ed1+1,ed1+m+1,cmp); 
    for(int i=1;i<=n;i++)
    f[i]=i;
    for(int i=1; i<=m; i++)
    if(find(ed1[i].x)!=find(ed1[i].y))
	{
        f[find(ed1[i].x)]=find(ed1[i].y);
        add_edge(ed1[i].x, ed1[i].y, ed1[i].dis);
        add_edge(ed1[i].y, ed1[i].x, ed1[i].dis); 
    }
    return ;
}

void dfs(int node)
{
    vis[node]=true;
    for(int i=head[node]; i; i=ed2[i].next)
	{
        int to=ed2[i].to;
        if(vis[to]) continue;
        deep[to]=deep[node]+1;
        fa[to][0]=node;
        w[to][0]=ed2[i].w;
        dfs(to);
    }
    return ;
}

int lca(int x, int y)
{
    if(find(x)!=find(y)) return -1;
    int ans=INF;
    if(deep[x]>deep[y]) swap(x,y);
    for(int i=20; i>=0; i--)
        if(deep[fa[y][i]]>=deep[x])
		{
            ans=min(ans, w[y][i]);
            y=fa[y][i];
        }
    if(x==y) return ans;
    for(int i=20; i>=0; i--)
        if(fa[x][i]!=fa[y][i])
		{
            ans=min(ans, min(w[x][i], w[y][i]));
            x=fa[x][i]; 
            y=fa[y][i];
        }
    ans=min(ans, min(w[x][0], w[y][0]));
    return ans;
}

int main()
{
    int x,y,z,q;
    cin>>n>>m;
    for(int i=1; i<=m; i++)
	{
        cin>>x>>y>>z;
        ed1[i].x=x;
        ed1[i].y=y;
        ed1[i].dis=z;
    }
    kruskal();
    for(int i=1;i<=n;i++)
        if(!vis[i])
		{
            deep[i]=1; 
            dfs(i);
            fa[i][0]=i;
            w[i][0]=INF;
        }
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++)
		{
            fa[j][i]=fa[fa[j][i-1]][i-1]; 
            w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]);
        }
    cin>>q;
    for(int i=1; i<=q; i++)
	{
		cin>>x>>y;
        cout<<lca(x,y)<<endl;
    }
    return 0;
} 

Day2

T1 积木大赛

思路

贪心

每往后走一个,想着把前面所有的填到跟他一个高度,这样就是最小填的次数

别忘了加最开始的第一堆积木的高度

#include<bits/stdc++.h>
using namespace std;
int n,a[100005];
long long ans=0;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)     
    cin>>a[i];
    for(int i=2;i<=n;i++)     
    if(a[i]>a[i-1]) 
    ans+=a[i]-a[i-1];
    cout<<ans+a[1];
    return 0;
}

T2 花匠

思路

真的贪心,假的DP

求一个最长波浪形子序列

贪心策略是,每个连续上升或者下降的子序列中,只有一个能选,所以我们直接统计一下波浪的数量,然后加上第一个就好了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
int n;
int fro,pro=0;
int ans;
int main()
{
	cin>>n;
	cin>>fro;
	for(int i=2;i<=n;i++)
	{
		int x;
		cin>>x;
		if(x==fro)
		continue;
		if(pro!=1&&x>fro)//上升 
		{
			ans++;
			pro=1;
		}
		else if(pro!=2&&x<fro)
		{
			ans++;
			pro=2;
		}
		fro=x;
	}
	ans++;
	cout<<ans<<'\n';
	return 0;
}

T3 华容道

BFS搜索加暴力巨恶心卡常

但说实话,正解我不会

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
#include <deque>
using namespace std;
const int maxn=35;
inline int read()
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}
inline void write(int x)
{
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10+'0');
}
struct node{
	int x,y,ddx,ddy,st;
};
int n,m,T;
int a[maxn][maxn];
bool vis[maxn][maxn][maxn][maxn];
int dx[]={0,0,1,0,-1};
int dy[]={0,1,0,-1,0};


int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>T;
	for(register int i=1;i<=n;i++)
	{
		for(register int j=1;j<=m;j++)
		cin>>a[i][j];
	}
	while(T--)
	{
		deque <node> q;
		bool flag=false;
		memset(vis,false,sizeof(vis));
		int ex,ey,sx,sy,tx,ty;
		cin>>ex>>ey>>sx>>sy>>tx>>ty;

		node now;
		now=(node){sx,sy,ex,ey,0};
		vis[ex][ey][sx][sy]=true;
		q.push_back(now);
		while(q.size())
		{
			now=q.front();
			q.pop_front();
			int x=now.x;
			int y=now.y;
			int lx=now.ddx;
			int ly=now.ddy;
			if(x==tx&&y==ty) 
			{
				flag=true;
				cout<<now.st<<'\n';
				break;
			}
			
			int fx=lx+0,fy=ly+1;
			int vx,vy;
			if(fx<n||fy<m||fx>1||fy>1)
			{
				vx=x,vy=y;
				if(x==fx&&y==fy)
				vx=lx,vy=ly;
				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
				{
					vis[fx][fy][vx][vy]=true;
					node nt;
					nt=(node){vx,vy,fx,fy,now.st+1};
					q.push_back(nt);
				}
			}
			
			fx=lx+1,fy=ly+0;
			if(fx<n||fy<m||fx>1||fy>1)
			{
				vx=x,vy=y;
				if(x==fx&&y==fy)
				vx=lx,vy=ly;
				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
				{
					vis[fx][fy][vx][vy]=true;
					node nt;
					nt=(node){vx,vy,fx,fy,now.st+1};
					q.push_back(nt);
				}
			}
			
			fx=lx+0,fy=ly-1;
			if(fx<n||fy<m||fx>1||fy>1)
			{
				vx=x,vy=y;
				if(x==fx&&y==fy)
				vx=lx,vy=ly;
				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
				{
					vis[fx][fy][vx][vy]=true;
					node nt;
					nt=(node){vx,vy,fx,fy,now.st+1};
					q.push_back(nt);
				}
			}
			
			fx=lx-1,fy=ly+0;
			if(fx<n||fy<m||fx>1||fy>1)
			{
				vx=x,vy=y;
				if(x==fx&&y==fy)
				vx=lx,vy=ly;
				if(a[fx][fy]&&!vis[fx][fy][vx][vy])
				{
					vis[fx][fy][vx][vy]=true;
					node nt;
					nt=(node){vx,vy,fx,fy,now.st+1};
					q.push_back(nt);
				}
			}
		}
		if(!flag) 
		cout<<-1<<'\n';
	}
	return 0;
}
本博文为wweiyi原创,若想转载请联系作者,qq:2844938982
原文地址:https://www.cnblogs.com/wweiyi2004/p/15542563.html