noip2013:noip2013题解 2024-05-02 14:01:23 0 0 Day1: 1.转圈游戏 (circle.cpp/c/pas) 【问题描述】 n 个小伙伴(编号从 0 到 n-1)围坐一圈玩游戏。按照顺时针方向给n 个位置编号,从 0 到 n-1。最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,……,依此类 推。 游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,……,依此类推,第n − m号位置上的小伙伴走到第0 号位置,第n-m+1 号位置上的小伙伴走到第 1 号位置,……,第 n-1 号位置上的小伙伴顺时针走到第m-1 号位置。 现在,一共进行了 10k 轮,请问 x 号小伙伴最后走到了第几号位置。 【输入】 输入文件名为 circle.in。 输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。 【输出】 输出文件名为 circle.out。 输出共 1 行,包含 1 个整数,表示 10k 轮后 x 号小伙伴所在的位置编号。 【输入输出样例】 circle.in 10 3 4 5 circle.out 5 【数据说明】 对于 30%的数据,0 < k < 7; 对于 80%的数据,0 < k < 107; 对于 100%的数据,1 < n< 1,000,000,0 <m <n ,0 ≤ x ≤ n,0 < k< 109。快速幂(关于快速幂的详细解说将在即将发出的博客:数论总结。中有说明): #include<cstdio>//快速幂 #include<algorithm>#include<cstring>using namespace std;long long n,m,k,x;int main(){freopen("circle.in","r",stdin);freopen("circle.out","w",stdout);scanf("%d %d %d %d",&n,&m,&k,&x);long long q=1,p=10;while(k>0){if(k%2!=0) q=(q*p)%n;k=k>>1;p=(p*p)%n;}x=(x+q*m)%n;printf("%d\n",x);return 0;} 2.火柴排队 (match.cpp/c/pas) 【问题描述】 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:,其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。 每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对 99,999,997 取模的结果。 【输入】 输入文件为 match.in。 共三行,第一行包含一个整数 n,表示每盒中火柴的数目。 第二行有 n 个整数,每两个整数之间用一个空格隔开,表示第一列火柴的高度。 第三行有 n 个整数,每两个整数之间用一个空格隔开,表示第二列火柴的高度。 【输出】 输出文件为 match.out。 输出共一行,包含一个整数,表示最少交换次数对 99,999,997取模的结果。 【输入输出样例 1】 match.in match.out 4 2 3 1 4 3 2 1 4 1 【输入输出样例说明】 最小距离是 0,最少需要交换 1 次,比如:交换第 1 列的前 2 根火柴或者交换第 2 列的前 2 根火柴。 【输入输出样例 2】 match.in match.out 4 1 3 4 2 1 7 2 4 2 【输入输出样例说明】 最小距离是 10,最少需要交换 2 次,比如:交换第 1 列的中间 2 根火柴的位置,再交换第 2 列中后 2 根火柴的位置。 【数据范围】 对于 10%的数据, 1 ≤ n ≤ 10; 对于 30%的数据,1 ≤ n ≤ 100; 对于 60%的数据,1 ≤ n ≤ 1,000; 对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤火柴高度≤ 231− 1。当了解了题目,可将问题转化为求交换相邻数字的长度。 典型的归并排序,从题目给出的公式我们可以证明到该题贪心的思路:用一个结构体记录每组的数据分别的编号和数值,分别按从小到大排序(可观察处按升序排列后对应两组数据的差值最小),排序后查找错位的点(即排序后两组对应数值编号不同的点),进行归并排序。 归并排序的大概思路:分别排序后合并排序。 #include<cstdio>//归并排序 #include<algorithm>#include<cstring>using namespace std;const int maxn=100005;const long long mod=99999997;struct node{int u,v;//记录位置和高度 }a[maxn],b[maxn],f[maxn]; //f记录第i个数移动后的位置 long long sb[maxn];long long n;long long ans=0;bool compare1(node a,node b){return a.v<=b.v;}bool compare2(node a,node b){return a.v>=b.v;}void work1(int y)//计算错位的个数 {//printf("y=%d\n",y);for(int i=y;i<=n;i+=i&-i)//如果i是奇数,变成偶数 {sb[i]++;//printf("i=%d sb=%d\n",i,sb[i]);}}int work2(int a){int ans=0;for(int i=a;i>=1;i-=i&-i){ans+=sb[i];//printf("%d\n",ans);}return ans;}int main(){freopen("match.in","r",stdin);freopen("match.out","w",stdout);scanf("%d",&n);for(long long i=1;i<=n;i++){scanf("%lld",&a[i].v);a[i].u=i;}for(long long i=1;i<=n;i++){scanf("%lld",&b[i].v);b[i].u=i;}for(int i=1;i<=n;i++){f[i].u=i;}sort(a+1,a+n+1,compare1);sort(b+1,b+n+1,compare1);for(int i=1;i<=n;i++){f[a[i].u].v=b[i].u;//printf("%d",f[a[i].u].v);//}sort(f+1,f+1+n,compare2);//不排序的话答案是错的= =woc for(int i=1;i <=n;i++){work1(f[i].u);ans+=work2(f[i].u-1);//printf("%d\n",f[i].u-1);//printf("%d\n",ans);ans=ans%mod;}printf("%d",ans);return 0;} 3.货车运输 (truck.cpp/c/pas) 【问题描述】 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。 【输入】 输入文件名为 truck.in。 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有n 座城市和 m 条道路。 接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。 接下来一行有一个整数 q,表示有 q 辆货车需要运货。 接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市 运输货物到 y 城市,注意:x 不等于 y。 【输出】 输出文件名为 truck.out。 输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。 【输入输出样例】 truck.in truck.out 4 3 3 1 2 4 -1 2 3 3 3 3 1 1 3 1 3 1 4 1 3 【数据说明】 对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000; 对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000; 对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。 最大生成树加最近公共祖先。 #include<cstdio>#include<algorithm>using namespace std;/* 更新: 一个是f数组的更新:f[i][j]=f[f[i][j-1]][j-1]; 一个是g数组的更新:g[i][j]=min(g[i][j-1],g[f[i][j-1][j-1]) (因为i结点的2^j的祖先节点就是i的2^(j-1)的祖先节点的2^(j-1)的祖先节点) 过程: 将两个点调到同一深度(将深度大的),当x==y是就可返回值ans */int n,m,q;int deep[100001];//i节点的深度int f[100001][32];//i节点的2^j的父亲节点int g[100001][32];//i节点到2^j祖先的最短小边;struct node{int u,v,w;}e[100001];int father[100001];int s[100001];int t[100001];int ans[100001];int head[100001];int k=0;struct node1{int to,next,value;}e1[500001];bool cmp(node e1,node e2){return e1.w>e2.w;//求最大按降序排列 }int find(int x)//找祖先{if(father[x]==x)return x;else return father[x]=find(father[x]);}void add(int u,int v,int w){k++;e1[k].to=v;e1[k].value=w;e1[k].next=head[u];head[u]=k;}int dfs(int x,int sum)//最近公共祖先查找 (起点是终点的父亲){deep[x]=sum;for(int i=1;i<=14;i++)//初始 {f[x][i]=f[f[x][i-1]][i-1];g[x][i]=min(g[f[x][i-1]][i-1],g[x][i-1]);}int p1=head[x];while(p1){if(deep[e1[p1].to]==0){f[e1[p1].to][0]=x;//该节点已无儿子= =,所以x是他的祖先g[e1[p1].to][0]=e1[p1].value;dfs(e1[p1].to,sum+1);}p1=e1[p1].next;}}int work(int x,int y)//最短路 {if(deep[x]>deep[y])//默认x的深度比y小1,所以进行一个判定 {int temp=x;x=y;y=temp;}int minn=2100000000;for(int i=14;i>=0;i--)//把一个较深的点向上提,并记录他到祖先的最小距离{if(deep[x]<=deep[f[y][i]]){minn=min(g[y][i],minn);y=f[y][i];}}if(x==y)return minn;for(int i=14;i>=0;i--){if(f[x][i]!=f[y][i]){minn=min(min(g[x][i],g[y][i]),minn);//查找最短路 x=f[x][i];y=f[y][i];}}minn=min(min(g[x][0],g[y][0]),minn);return minn;}void read(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);}for(int i=1;i<=n;i++){father[i]=i;//父节点初始 }int cnt=0;sort(e+1,e+m+1,cmp);for(int i=1;i<=m;i++){int fx=find(e[i].u);int fy=find(e[i].v);if(fx!=fy){father[father[e[i].u]]=father[e[i].v];cnt++;add(e[i].u,e[i].v,e[i].w);add(e[i].v,e[i].u,e[i].w);}if(cnt==n-1)break;}for(int i=1;i<=n;i++){if(deep[i]==0)dfs(i,1);}scanf("%d",&q);for(int i=1;i<=q;i++){scanf("%d%d",&s[i],&t[i]);if(find(s[i])!=find(t[i]))ans[i]=-1;elseans[i]=work(s[i],t[i]);printf("%d\n",ans[i]);}}int main(){//freopen("truck.in","r",stdin);//freopen("truck.out","w",stdout);read();return 0;}/*4 31 2 42 3 33 1 131 31 41 33-13*/ Day2: 1.积木大赛 (block.cpp/c/pas) 【题目描述】 春春幼儿园举办了一年一度的“积木大赛”。今年比赛的内容是搭建一座宽度为n的大厦,大厦可以看成由n块宽度为1的积木组成,第i块积木的最终高度需要是ℎi。 在搭建开始之前,没有任何积木(可以看成块高度为 0 的积木)。接下来每次操作,小朋友们可以选择一段连续区间[L,R],然后将第L块到第R块之间(含第 L 块和第 R 块)所有积木的高度分别增加1。 小M是个聪明的小朋友,她很快想出了建造大厦的最佳策略,使得建造所需的操作次数最少。但她不是一个勤于动手的孩子,所以想请你帮忙实现这个策略,并求出最少的操作次数。 【输入】 输入文件为 block.in 输入包含两行,第一行包含一个整数n,表示大厦的宽度。 第二行包含n个整数,第i个整数为ℎi。 【输出】 输出文件为 block.out 仅一行,即建造所需的最少操作数。 【输入输出样例】 block.in block.out 5 2 3 4 1 2 5 【样例解释】 其中一种可行的最佳方案,依次选择 [1,5] [1,3] [2,3] [3,3] [5,5] 【数据范围】 对于 30%的数据,有1 ≤ n ≤ 10; 对于 70%的数据,有1 ≤ n ≤ 1000;对于 100%的数据,有1 ≤ n ≤100000,0 ≤ hi ≤ 10000。 #include<cstdio>//二分思想的暴力= = #include<algorithm>#include<cstring>using namespace std;const int maxn=100005;const int inf=0x7f7f7f;int n;int h[maxn];int ans;int minn;bool visit[maxn];void work(int l,int r){if(l>r) return;minn=inf;for(int i=l;i<=r;i++){if(visit[i]==false){minn=min(minn,h[i]);}}if(minn==inf) return;//ans+=minn;for(int i=l;i<=r;i++) h[i]-=minn;//for(int i=l;i<=r;i++){if(visit[i]==false&&h[i]==0){visit[i]=true;work(l,i-1);work(i+1,r);}}return ;}int main(){freopen("block.in","r",stdin);freopen("block.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&h[i]);}memset(visit,false,sizeof(visit));work(1,n);printf("%d",ans);return 0;} 2.花匠 (flower.cpp/c/pas) 【问题描述】 花匠栋栋种了一排花,每株花都有自己的高度。花儿越长越大,也越来越挤。栋栋决定把这排中的一部分花移走,将剩下的留在原地,使得剩下的花能有空间长大,同时,栋栋希望剩下的花排列得比较别致。 具体而言,栋栋的花的高度可以看成一列整数ℎ1, ℎ2, … , ℎn。设当一部分花被移走后,剩下的花的高度依次为g1,g2,… , gm,则栋栋希望下面两个条件中至少有一个满足: 条件 A:对于所有的1≤i≤,有g2i >g2i-1,同时对于所有的1≤i≤,有g2i >g2i+1; 条件 B:对于所有的1≤i≤,有g2i < g2i-1,同时对于所有的1≤i≤,有g2i <g2i+1。 注意上面两个条件在 m = 1时同时满足,当 m> 1时最多有一个能满足。 请问,栋栋最多能将多少株花留在原地。 【输入】 输入文件为 flower.in。 输入的第一行包含一个整数,表示开始时花的株数。 第二行包含个整数,依次为ℎ1, ℎ2, … , ℎn,表示每株花的高度。 【输出】 输出文件为 flower.out。 输出一行,包含一个整数,表示最多能留在原地的花的株数。 【输入输出样例】 flower.in flower.out 5 5 3 2 1 2 3 【输入输出样例说明】 有多种方法可以正好保留 3 株花,例如,留下第 1、4、5 株,高度分别为 5、1、2,满足条件 B。 【数据范围】 对于 20%的数据,n ≤ 10; 对于 30%的数据,n ≤ 25; 对于 70%的数据,n ≤ 1000,0 ≤ ℎn≤ 1000; 对于 100%的数据,1 ≤ n ≤ 100,000,0 ≤ ℎn≤ 1,000,000,所有的ℎn随机生成,所有随机数服从某区间内的均匀分布。贪心加动归: 开始忘了加h[i]==h[i-1] 的情况,所以WA了2个点。 #include<cstdio>#include<algorithm>#include<cstring>using namespace std;const int maxn=100005;int n;int h[maxn];int dp[maxn][2];void work(){ for(int i=1;i<=n;i++) { dp[i][0]=dp[i-1][0]; if(i==1||h[i]<h[i-1]) { dp[i][0]=max(dp[i-1][0],dp[i-1][1]+1); }dp[i][1]=dp[i-1][1]; if(i==1||h[i]>h[i-1]) { dp[i][1]=max(dp[i-1][1],dp[i-1][0]+1); } }}int main(){freopen("flower.in","r",stdin);freopen("flower.out","w",stdout);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&h[i]);}work();printf("%d\n",max(dp[n][0],dp[n][1]));return 0;} 收藏(0)