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;
}

NOIP2013题解的更多相关文章

  1. NOIP2013 题解

    转圈游戏 题解:快速幂 #include <cstdio> int n, m, k, x; inline long long QuickPow(int a, int k, int MOD) ...

  2. [NOIP补坑计划]NOIP2013 题解&做题心得

    场上预计得分:100+100+100+100+100+60=560(省一分数线410) 五道傻逼题+一道大搜索题…… 题解: D1T1 转圈游戏 题面 水题送温暖~ #include<algor ...

  3. [NOIP2013提高&洛谷P1966]火柴排队 题解(树状数组求逆序对)

    [NOIP2013提高&洛谷P1966]火柴排队 Description 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相 ...

  4. NOIP2013 DAY2题解

    DAY2 T1积木大赛 传送门 题目大意:每次可以选区间[l,r]加1,最少选几次,让每个位置有 它应有的高度. 题解:O(n)扫一遍就好了.后一个比前一个的高度低,那么前一个已经把它覆盖了, 如果高 ...

  5. [NOIP2013]华容道 题解(搜索)

    [NOIP2013]华容道 [题目描述] 这道题根据小时候玩华容道不靠谱的经验还以为是并查集,果断扑街.考后想想也是,数据这么小一定有他的道理. 首先由于是最小步数,所以BFS没跑了.那么我们大可把这 ...

  6. [NOIP2013]华容道 题解

    [NOIP2013]华容道 首先是一种比较显然的做法. 整个棋盘,除了起点,终点和空格,其他的方块是等价的. 对于终点,它始终不会变化,如果搜到终点结束搜索即可,所以我们不需要考虑终点. 所以需要考虑 ...

  7. 题解 【NOIP2013】转圈游戏

    [NOIP2013]转圈游戏 Description n个小伙伴(编号从0到n-1)围坐一圈玩游戏.按照顺时针方向给n个位置编号,从0到n-1.最初,第0号小伙伴在第0号位置,第1号小伙伴在第1号位置 ...

  8. 题解【洛谷P1967】[NOIP2013]货车运输

    题面 题解 注意到有一些限重很低的边不会被走到. 于是考虑建一棵最大生成树,在生成树上寻找答案. 设\(f[i][j]\)表示\(i\)的\(2^j\)级祖先,\(w[i][j]\)表示\(i\)到\ ...

  9. 题解【洛谷P1983】[NOIP2013]车站分级

    题面 题解 不难想到拓扑排序 于是每一个等级高的向等级低的连一条边 考虑拓扑排序过程中的分层 对于每个点进行分层 于是答案就是这些点中的最大层数 然后就会RE 发现我们多连了一些重复的边 用一个标记数 ...

随机推荐

  1. 一个比较变态的js传值,Query的bind、ajax闭包、上下文传值

    var getIDNameList = function (list, selected, text, btn, actionUrl, defaultKey, deleteKey, keyName, ...

  2. vue 动态加载组建

    <component :is="comp1"></component> data () { return { comp1:'', } } require.e ...

  3. 如何构造分层次的 Json 数据

    十年河东,十年河西,莫欺骚年穷...打错个字~_~ 现有如下需求,构造分层次的Json数据,层次结构类似下图: 上图使用EasyUI生成的,静态HTML如下: <html xmlns=" ...

  4. [Oacle][Partition]Partition操作与 Index, Global Index 的关系

    [Oacle][Partition]Partition操作与 Index, Global Index 的关系: ■ Regarding the local index and the global i ...

  5. ASP.NET Core使用TopShelf部署Windows服务

    asp.net core很大的方便了跨平台的开发者,linux的开发者可以使用apache和nginx来做反向代理,windows上可以用IIS进行反向代理. 反向代理可以提供很多特性,固然很好.但是 ...

  6. 【DDD】使用领域驱动设计思想实现业务系统

    最近新接了一个业务系统——社区服务系统,为了快速熟悉和梳理老系统的业务逻辑和代码,同时对老系统代码做一些优化,于是打算花上一个月时间不间断地对老系统服务进行重构.同时,考虑到社区业务的复杂性,想起了之 ...

  7. Dethe is my Finaunce金融

    英国诗人乔叟Dethe is my Finaunce金融 英语中“金融”在14世纪,金融计算时间价值的手段.就随机结果签约的能力.一个允许转让金融权后的清算.<Lamentation of Ma ...

  8. Jvm 10 升级笔记

    移除了 JPEGCodec https://www.cnblogs.com/liaolongjun/p/6878359.html

  9. spring-session-data-redis包冲突

    包冲突 spring 的包很容易冲突, 因为写软件的人在兼容性上处理的不够,一般不检测重复加载. spring-session-data-redis 引用后, 一定要把 spring-session ...

  10. 5 questions

    1.软件开发中有哪几种过程模型? 2.详细设计有哪几种描述方法? 3.什么是需求分析? 4.软件设计的基本原理包括哪些内容? 5.简述文档在软件工程中的作用? 逸翔.