A题 Simple

时间限制:1000ms | 空间限制:256MB

问题描述

对于给定正整数\(n,m\),我们称正整数\(c\)为好的,当且仅当存在非负整数\(x,y\)使得\(n×x+m×y=c\)。

现在给出多组数据,对于每组数据,给定\(n,m,q\),求\([1,q]\)内有 多少个正整数不是好的。

输入格式

第一行,一个整数\(T\)表示数据组数。

接下来每行三个数,分别表示\(n,m,q\),即一组询问。

输出格式

对于输入的每组数据,输出一行表示答案。

数据规模

对于30%的数据,\(n,m,q\le100\);

对于60%的数据,\(n,m,q\le100000\);

对于100%的数据,\(n\le10^5,m\le10^9, q\le10^{18} ,T\le10\);

样例

样例输入
2
78 100 4
70 3 34
样例输出
4
23

题解

先上60分代码代码,当我们选取\(1到q\)中的某一个数\(i\)时,如果\(i\)是好的,则必然满足\(i\)>\(min(n,m)\)。这时我们让\(i\)减去\(max(n,m)\)的\(j\)倍得到一个数\(x\),若\(x\)%$ min(n,m)=0$就可以说明这个数是一个好数。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. inline char get(){
  4. static char buf[30],*p1=buf,*p2=buf;
  5. return p1==p2 && (p2=(p1=buf)+fread(buf,1,30,stdin),p1==p2)?EOF:*p1++;
  6. }
  7. inline long long read(){
  8. register char c=get();long long f=1,_=0;
  9. while(c>'9' || c<'0')f=(c=='-')?-1:1,c=get();
  10. while(c<='9' && c>='0')_=(_<<3)+(_<<1)+(c^48),c=get();
  11. return _*f;
  12. }
  13. //利用fread优化的超级快读↑
  14. long long n,m,q;
  15. long long good;
  16. bool pd;
  17. long long t;
  18. int main(){
  19. //freopen("1.txt","r",stdin);
  20. t=read();
  21. //cout<<t<<endl;
  22. while(t--){
  23. good=0;
  24. n=read();m=read();q=read();
  25. if(n==1 || m==1){
  26. printf("0\n");
  27. continue;
  28. }//特判
  29. if(n<m)swap(n,m);
  30. //cout<<1<<endl;
  31. for(register long long i=m;i<=q;i++){
  32. pd=0;
  33. for(register long long j=0;j*n<=i;j++){
  34. if((i-(j*n)) % m==0){
  35. good++;
  36. break;
  37. }
  38. }
  39. }
  40. printf("%lld\n",q-good);
  41. }
  42. return 0;
  43. }

因为各种玄学优化可以得到60分。这个时候让我们换一种思路:

首先考虑的是dp,这个时候看下数据范围\(q\le10^{18}\),很显然不行。如果用数位dp的话限制条件又过多,于是pass掉dp的思路。

稍微思考一下公式,\(nx+my=c\),我们令\(n>m\),则一定有\(n=m*k+t\);由于\(m\le100000\),则一定存在\(t<=100000\)。

这时我们用一个数组f[x]记录满足\(n*j\)%\(m=x\)里最小的值\(n*j\)。因为\(x\)的值最大为100000,所以f[x]的值最后总会循环出现。再稍加推导就可以知道:

若一个数\(c\)是好的,那么他的倍数\(c*k\)也一定是好的。

那么我们让\(ans+=(q/c)\)

问题还可以进一步简化。

我们知道一定存在至少一个\(f_i≠0\)

这时我们保证好数\(c\)中一定有至少0个\(n\)

那么我们就让\(c-f_i\),此时即可保证\(c\)一定是\(n\)的倍数。剩余的量全部分配给\(m\)即可。也就是\(ans+=(q-f[i])/m+1\);

附上代码
  1. #include<bits/stdc++.h>
  2. #define ll long long
  3. using namespace std;
  4. inline char get(){
  5. static char buf[30],*p1=buf,*p2=buf;
  6. return p1==p2 && (p2=(p1=buf)+fread(buf,1,30,stdin),p1==p2)?EOF:*p1++;
  7. }
  8. inline ll read(){
  9. register char c=get();register ll f=1,_=0;
  10. while(c>'9' || c<'0')f=(c=='-')?-1:1,c=get();
  11. while(c<='9' && c>='0')_=(_<<3)+(_<<1)+(c^48),c=get();
  12. return _*f;
  13. }
  14. ll f[100005];
  15. ll n,m,q;
  16. int main(){
  17. //freopen("1.txt","r",stdin);
  18. ll t;
  19. t=read();
  20. //cout<<1<<endl;
  21. while(t--){
  22. n=read();m=read();q=read();
  23. if(n<m)swap(n,m);
  24. if(n>q && m>q)printf("%lld\n",q);
  25. else if(n>q && m<=q)printf("%lld\n",q-q/m);
  26. else{
  27. for(register int i=0;i<100005;i++)f[i]=-1;//单纯for循环似乎复杂度比memset低一些?
  28. for(register int i=0;;i++){
  29. ll now=n*i;
  30. if(f[now%m]!=-1)break;
  31. f[now%m]=now;
  32. }
  33. ll ans=0;
  34. for(register int i=0;i<m;i++){
  35. if(f[i]!=-1&&(q-f[i]>=0))ans+=(q-f[i])/m+1;
  36. }
  37. printf("%lld\n",q-ans+1); //因为c的取值是[1,q],也就是说 n*x+y*m=c => x,y不同为0
  38. }
  39. }
  40. return 0;
  41. }

##B题 Walk
######时间限制:2000ms | 空间限制:256MB
####问题描述
给定一棵有$n$个节点的树,每条边的长度为1,同时有一个权值$w$,定义一条路径的权值为路径上所有边的权值的最大公约数,现在对于任意$i∈[1,n]$,求树上所有长度为$i$的简单路径中权值最大的值。如果不存在长度为i的路径则输出0。
####输入格式
第一行,一个整数$n$表示树的大小
接下来$n-1$行,每行三个整数$u,v,w$表示$u$和$v$之间有一条权值为$w$的边。
####输出格式
对于每种长度,输出一行,表示答案。
####数据规模
对于30%的数据,$n\le1000$;
对于额外30%的数据,$w\le100$;
对于100%的数据,$n\le4*10^5,1\le u,v\le n,w\le10^6$;
####样例
|样例输入
|-----
|3
1 2 3
1 3 9

样例输出
3
9
0

题解(转,不会树DP很多年QAQ)

这道题我们可以考虑枚举最大公因数,然后求最大公因数为i的最长链。这个需要怎么办呢?我们考虑将每条边拆成因数条边,要是一次性加入的话数量会炸飞,所以我们每次将是当前公因数倍数的边加入,然后求树上的最长链。然后用当前的公因数更新长度为最长链的长度的答案。求解后将数组清零,重复利用。

有些长度可能不会出现在最长链中,因为他是构成最长链的一部分,所以如果这个长度的答案<长度大于他的答案的话,就要将长度大于他的答案给他,从大到小枚举\(ans[i]=max(ans[i],ans[i+1])\)

  1. #include<iostream>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cmath>
  6. #define N 800003
  7. #define M 1000006
  8. using namespace std;
  9. int head[M],nxt1[N],x1[N],y2[N],cnt,sz,ans[N];
  10. int point[M],nxt[N],v[N],tot,st[N],top,vis[N],len;
  11. int n,m,mark;
  12. void add(int k,int x,int y)
  13. {
  14. cnt++; nxt1[cnt]=head[k]; head[k]=cnt; x1[cnt]=x; y2[cnt]=y;
  15. }
  16. void add1(int x,int y)
  17. {
  18. tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
  19. tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
  20. st[++top]=x; st[++top]=y;
  21. }
  22. int dfs(int x,int fa)
  23. {
  24. vis[x]=sz;
  25. int l=0,r=0;
  26. for (int i=point[x];i;i=nxt[i])
  27. if (vis[v[i]]!=sz&&v[i]!=fa){
  28. int t=dfs(v[i],x);
  29. if (t+1>l) r=l,l=max(l,t+1);
  30. else if (t+1>r) r=max(r,t+1);
  31. }
  32. len=max(len,l+r);
  33. return l;
  34. }
  35. int main()
  36. {
  37. freopen("a.in","r",stdin);
  38. freopen("my.out","w",stdout);
  39. scanf("%d",&n);
  40. for (int i=1;i<n;i++)
  41. {
  42. int a,b,c; scanf("%d%d%d",&a,&b,&c);
  43. add(c,a,b);
  44. }
  45. for (int i=1;i<=1000000;i++){
  46. for (int j=i;j<=1000000;j+=i)
  47. for (int k=head[j];k;k=nxt1[k])
  48. add1(x1[k],y2[k]);
  49. mark=i; ++sz; len=0;
  50. for (int j=1;j<=top;j++)
  51. if (vis[st[j]]!=sz) dfs(st[j],0);
  52. ans[len]=max(ans[len],i);
  53. for (int j=1;j<=top;j++)
  54. point[st[j]]=0;
  55. tot=0; top=0;
  56. }
  57. for (int i=n;i>=1;i--) ans[i]=max(ans[i],ans[i+1]);
  58. for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
  59. }

##C题 Travel
######时间限制:1000ms | 空间限制:256MB
####问题描述
给定一个长度为$n$的序列$x_1,x_2,...,x_n$。每一次 Lyra 可以选择向左跳到任意一个还没到过的位置,也可以向右跳到任意一个还没到过的位置。如果现在Lyra在格子$i$,她下一步跳向格子$j$,那么这次跳跃的花费为$|xi−xj|$。注意,跳意味着格子**$i$**和格子$j$中间其他的格子都不会被这次跳跃影响。并且,Lyra不应该跳出边界。
Lyra的初始位置在格子$s$。Lyra将会在到访过所有格子恰好一次之后,在某个位置停下来,这样就完成了任务。
Lyra想知道如果她一共向左跳了$L$次,那么她要完成任务的最小总花费是多少,并希望你输出任意一种花费最小的方案。特殊的,如果 Lyra 没有办法完成任务,请输出一行 $−1$。
####输入格式
第一行,三个整数$n,L,s$,分别表示序列的大小,向左走的次数,和初始位置。
第二行,$n$个数字,表示序列$x_i$。
####输出格式
第一行,一个数字,表示答案。
如果能完成任务,则第二行,输出$n-1$个数字,表示方案。注意,Lyra 初始的位置已经确定了,所以不要输出。
####数据规模
对于所有数据,都满足$x_1 1 2 3

样例输出
3
1 3

题解

先上15分的代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4. cout<<-1<<endl;
  5. return 0;
  6. }

足以说明数据有多水。

好的不皮了,我们来讲正解。

首先当\(x_i=i\)时,数列是等差的递增数列,每个的间距相同,所以我们肯定是考虑尽量的不走回头路,但是因为必须要向左走\(L\)步,所以至少有\(L\)段要被经过三次,那么我们从起点一直向左跳,跳到不能再跳为止,如果此时还是不够的话,我们就再跳到起点的右边正好多出剩下\(L\)步的为止,然后再向左跳,最后在向右跳即可。如果连续向左跳会多,那就考虑间隔的跳最终跳到头再转弯即可。-1的情况非常好处理,当\(l=0\)但是\(s≠1\)的时候,是没有合法路径的。同理\(l=n-1\),\(s≠n\)也是没有合法路径的。



常规情况比较难考虑,让我们先来考虑特殊情况。当\(s=1\)的时候,显然答案的下界是 \(x_n-x_1\),就是从最左边按次序一直跳到最右边,不过L>0的时候就不能这么做了。答案要求最小也就是说要尽量少走回头路。假如我们在\(n\)这个位置停下,那么中间就需要走一些回头的路类来用掉\(L\),我们把所有\(i\)到\(i+1\)之间的区间看成一个线段。跳的路径相当于对线段进行覆盖。显然所有的线段都必须覆盖至少1次,而至少有\(L\)个线段至少覆盖3次。



但是实际情况中,起点不一定是1,终点也不一定是\(n\)。我们假设终点\(t\)再起点\(s\)的左边,那么\([1,s-1][t+1,n]\)中的点至少都需要经过两次,如果能在这两段中用掉较多的\(L\),那我们中间剩下的L就会少,这样在好不过。以为中间\([s,t]\)这一段的处理就相当于是上面[1,n]的处理,中间的线段都会被覆盖三次。那么两边最多会向左走\(n-t+s-1\)步,如果\(n-t+s-1>=L\),那么中间的不需要向左走,直接一步一步的跳就好了。如果不够\(L\)步的话就考虑从中选取差值小的线段让其长度*3即可。然后根据对称原则,在计算终点在起点左边的情况,这时候其实就是向右跳\(n-L-1\)步,向上面一样处理即可。



然后我们就可以通过枚举终点,来更新答案。对于上面中起点相对位置的两种情况,我们单独看。每次就是在上一起点的基础上中间加入\([i,i+1]\),然后\([t+1,n]\)的长度减小\([i,i+1]\),我们用小根堆维护,每次都尽可能利用小的。注意有可能存在无法满足答案的情况,就是堆中不够。



还需要注意的是中的段的头尾两条线段不能使用,因为无论怎么跳都没法满足,会影响两边的跳法。

  1. #include <stdio.h>
  2. #include <iostream>
  3. #include <algorithm>
  4. #include <memory.h>
  5. #include <string.h>
  6. #include <vector>
  7. using namespace std;
  8. typedef long long LL;
  9. typedef pair<int,int> mp;
  10. #define pb push_back
  11. const LL inf = 1ll<<50;
  12. const int maxn = 200005;
  13. int n,l,s,pos[maxn];
  14. int x[maxn],ans1[maxn];
  15. int y[maxn],ans2[maxn];
  16. mp ord[maxn];
  17. bool tag[maxn];
  18. LL solve(int n,int l,int s,int x[],int ans[]) {
  19. int cnt=0,tot=0;
  20. if (l<s) {
  21. for (int i=s-1;i>s-l;i--) ans[++cnt]=i;
  22. for (int i=1;i<=s-l;i++) if (i!=s) ans[++cnt]=i;
  23. for (int i=s+1;i<=n;i++) ans[++cnt]=i;
  24. return (LL)x[n]-x[1]+x[s]-x[1];
  25. }
  26. l-=s-1;
  27. if (l==n-s-1) {
  28. for (int i=s-1;i>=1;i--) ans[++cnt]=i;
  29. for (int i=n;i>s;i--) ans[++cnt]=i;
  30. return (LL)x[n]-x[1]+x[s]-x[1]+x[n]-x[s+1];
  31. }
  32. for (int i=s+1;i<n-1;i++) ord[++tot]=mp(x[i+1]-x[i],i+1);
  33. sort(ord+1,ord+tot+1);
  34. for (int i=1;i<=tot;i++) pos[ord[i].second]=i;
  35. LL minv=inf,sum=0;int e,j;
  36. for (int i=1;i<=l;i++) sum+=ord[i].first;
  37. minv=sum*2;e=n;j=l;
  38. for (int i=n-1,p=l;i>=n-l;i--) {
  39. if (pos[i]<=p) sum-=ord[pos[i]].first;
  40. else sum-=ord[p--].first;
  41. while (p&&ord[p].second>=i) --p;
  42. if (sum*2+x[n]-x[i]<minv) {
  43. minv=sum*2+x[n]-x[i];e=i;j=p;
  44. }
  45. }
  46. memset(tag,false,sizeof tag);
  47. for (int i=s-1;i>=1;i--) ans[++cnt]=i;
  48. for (int i=s+2;i<e;i++) if (pos[i]<=j) tag[i]=true;
  49. for (int i=s+1;i<e;i++)
  50. if (!tag[i+1]) ans[++cnt]=i;
  51. else {
  52. int tmp=i+1;while (tag[tmp]) ++tmp;
  53. for (int j=tmp-1;j>i;j--) ans[++cnt]=j;
  54. ans[++cnt]=i;i=tmp-1;
  55. }
  56. for (int i=n;i>=e;i--) ans[++cnt]=i;
  57. return (LL)x[n]-x[1]+x[s]-x[1]+minv;
  58. }
  59. int main()
  60. {
  61. #ifndef ONLINE_JUDGE
  62. freopen("travel.in","r",stdin);
  63. freopen("travel.out","w",stdout);
  64. #endif
  65. scanf("%d %d %d",&n,&l,&s);
  66. for (int i=1;i<=n;i++) scanf("%d",&x[i]);
  67. for (int i=1;i<=n;i++) y[i]=-x[n-i+1];
  68. if (s!=1&&l==0) {puts("-1");return 0;}
  69. if (s!=n&&l==n-1) {puts("-1");return 0;}
  70. LL cost1=solve(n,l,s,x,ans1);
  71. LL cost2=solve(n,n-1-l,n-s+1,y,ans2);
  72. if (cost1<cost2) {
  73. printf("%lld\n",cost1);
  74. for (int j=1;j<n;j++)
  75. printf("%d ",ans1[j]);
  76. }
  77. else {
  78. printf("%lld\n",cost2);
  79. for (int j=1;j<n;j++)
  80. printf("%d ",n-ans2[j]+1);
  81. }
  82. return 0;
  83. }

NOIP2018 集训(一)的更多相关文章

  1. NOIp2018集训test-10-24(am&pm)

    李巨连续AK三场了,我跟南瓜打赌李巨连续AK七场,南瓜赌李巨连续AK五场. DAY1 T1 qu 按题意拿stack,queue和priority_que模拟即可.特判没有元素却要取出的情况. T2 ...

  2. NOIP2018 集训(三)

    A题 Tree 问题描述 给定一颗 \(n\) 个点的树,树边带权,试求一个排列 \(P\) ,使下式的值最大 \[\sum_{i=1}^{n-1} maxflow(P_i, P_{i+1}) \] ...

  3. NOIP2018 集训(二)

    A题 神炎皇 问题描述 神炎皇乌利亚很喜欢数对,他想找到神奇的数对. 对于一个整数对 \((a,b)\) ,若满足 \(a+b\leq n\) 且 \(a+b\) 是 \(ab\) 的因子,则称 为神 ...

  4. NOIp2018集训test-10-18 (bike day4)

    这是一套简单题,这几天的考试让bike老爷感觉很绝望,说实话我也确实不知道还能怎么更简单了. 这几天的题换做llj.sxy应该都能轻松AK吧,至少随便考个250+应该不是问题吧,我越来越觉得觉得我跟他 ...

  5. NOIp2018集训test-10-17 (bike day3)

    发现自己gradully get moodier and moodier了 负面情绪爆发地越来越频繁,根本out of control,莫名其妙地就像着了魔一样 为什么用英语大概是因为今天早上早自习因 ...

  6. NOIp2018集训test-10-16 (bike day2)

    “毕姥爷:今天的题好简单啊,你们怎么考得这么烂啊,如果是noip你们就凉透了啊“ 今天的题难度应该是3.2.1递减的,但是我不知道哪根筋没搭对,平时我最多1h多就弃题了,今天硬生生写了2h20min的 ...

  7. [雅礼NOIP2018集训] day6

    打满暴力好像是一种挑战,已经连续几天考试最后一个小时自闭了,因为自以为打完了暴力,然而,结果往往差强人意 大概是考试的策略有些问题 T1: 我们设$g[x]$为在x时取小于等于m个物品的最大价值,下面 ...

  8. [雅礼NOIP2018集训 day4]

    感觉状态极差啊,今天居然爆零了 主要是以下原因: 1.又是T1看错题肝了两个小时,发现题意理解错误瞬间心态爆炸 2.T2交错了文件名 3.T3暴力子任务和正解(假的)混在一起,输出了两个答案 都想为自 ...

  9. [雅礼NOIP2018集训 day1]

    现在才来填坑,之后还要陆续补其他几天的,可能前几天真的太颓了 T1: 题目大意:给定一个长度为n的序列,m次询问每次询问给出l,r,询问区间l到r的元素在模k意义下的最大值 数据范围当然是你暴力写不过 ...

随机推荐

  1. 解析xml文件,修改Jenkins的配置

    最近因为服务器移动,在Jenkins中配置的一些地址之类的,都要改变,如图,我因为使用插件Sidebar Links增加一个链接地址,现在地址变了,所以在Jenkins中配置就需要改动link url ...

  2. 【转】android四大组件--ContentProvider详解

    一.相关ContentProvider概念解析: 1.ContentProvider简介在Android官方指出的Android的数据存储方式总共有五种,分别是:Shared Preferences. ...

  3. scrapy install

    csf@ubuntu:~$ sudo apt install python-scrapy

  4. System.TimeDate

    本篇将介绍时间类型. msdn官网:点击查看 时间辅助类:点击查看  方法:计算两个时间的时间差(年月日小时分钟),获取时间戳,时间格式转换,获取时间随机码 定义:表示时间上的一刻,通常以日期和当天的 ...

  5. python显示灰度图

    import matplotlib import matplotlib.pyplot as plt %matplotlib inline im=plt.imread('../lena.jpg', py ...

  6. Android Studio项目中三种依赖的添加方式

    通常一个AS项目中的依赖关系有三种,一是本地依赖(主要是对本地的jar包),二是模块依赖,三是远程依赖:添加这些依赖的目的在于上我们想要在项目的某一个模块中使用其中的功能,比如okttp这个网络框架库 ...

  7. windows下配置kafka

    https://blog.csdn.net/evankaka/article/details/52421314

  8. LunaSchedule记录

    博客访问量突破10000!!!(值得高兴一下 用一学期超级课程表,被50+M的内存占用,巨慢的加载速度给弄烦了,就自己开发了一款课程表管理程式 添加日历订阅,自动导入到系统日历,无需安装任何app L ...

  9. 【赛时总结】 ◇赛时·III◇ AtCoder ABC-099

    ◆赛时·III◆ ABC-099 ■唠叨■ 不要问我为什么先给ABC-100写了博客再写的ABC-099-- 莫名觉得这次比赛特别简单--虽然我并没有参加比赛,只是之后再补做的.QwQ ■试题& ...

  10. poj_2689_Prime Distance

    The branch of mathematics called number theory is about properties of numbers. One of the areas that ...