【解题报告】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