Codeforces Round #319 (Div. 1) 简要记录
2015-09-18 17:23
288 查看
A. Vasya and Petya’s Game
题意:Vasya和Petya在玩一个游戏。
Vasya在1~n中任意选一个数x,并且Petya要来猜这个数x。
Petya可以进行诸如此类的询问:”x能否被y整除”,Vasya只会回答yes或者no。
询问:
Petya最少需要问几次才能确定数x,并且输出他询问的数都是什么,有多种顺序输出一种即可。
解析:
这显然是个找规律题…
我们发现对于一个能够分出来两个不同质因数的数我们只要对质因数及质因数的几次幂进行询问即可确定该数。
所以我们把目光放在质因数上。
假设我们询问了n以下所有质因数的一次幂,形如PiP_i,则我们一定不能确定Pi2,P3i,P4i.....{P_i}^2,P_i^3,P_i^4.....
如果紧接着询问了所有质因数的二次幂,形如P2iP_i^2,则我们一定不能确定Pi3,P4i.......{P_i}^3,P_i^4.......
所以经由上面的规律寻找大概猜测我们要询问所有质因子的x次幂,并且该质因子的x次幂小于等于n。
大概脑补可以想象正确性。
直接暴力搞就行(为了尊重题我写了个线性筛素数23333)
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 1100 using namespace std; int n; int cnt; int print ; int tot; int prime ; int v ; void sieve() { for(int i=2;i<=n;i++) { if(!v[i]) { prime[++tot]=i; } for(int j=1;j<=tot&&i*prime[j]<=n;j++) { v[i*prime[j]]=1; if(i%prime[j]==0)continue; } } } int main() { scanf("%d",&n); sieve(); for(int i=1;i<=tot;i++) { int tmp=prime[i]; while(tmp<=n) { print[++cnt]=tmp; tmp*=prime[i]; } } printf("%d\n",cnt); for(int i=1;i<=cnt;i++)printf("%d ",print[i]); puts(""); }
B. Invariance of Tree
题意:有一棵大小为n的无向树,我们称一棵树invariantinvariant relativerelative toto一个序列p = p1p2...pnp = p_1p_2... p_n当且仅当树上任意一条边(u,v)(u,v),边(pu,pv)(p_u,p_v)也在这棵树上。
询问:
给定序列求该树,不存在则输出NO,否则输出YES并且输出任意一种情况。
解析:
既然是一棵树,那么一定无环,也就是说不能没有大小为1或者2的置换,如果没有的话该树一定存在环,所以我们只需要寻找是否存在大小为1或者2的置换就好了。
如果存在大小为1的置换的话,即把所有其他的点连向它即可。
如果不存在大小为1的置换,反而存在大小为2的置换的话,首先将二者的边连上。
然后枚举其他置换。
我们发现其他置换的奇数点与第一个点连的话,那么所有的奇数点的下一个点都应该与第二个点连。(或者相反:D)
所以如果其他置换中有一个置换的大小为奇数则不能满足题意,输出NO。
否则按照如上方法输出边即可。
如果大小为1,大小为2的置换均不存在直接输出NO即可。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 100010 using namespace std; int n; int a ; int v ; int sta ; int tag ; int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); int flag=0; for(int i=1;i<=n;i++) { if(a[i]==i) { flag=1; puts("YES"); for(int j=1;j<=n;j++) if(j!=i)printf("%d %d\n",i,j); break; } } if(flag)return 0; int tmp=-1; for(int i=1;i<=n;i++) { if(a[a[i]]==i) { tmp=i;break; } } if(tmp==-1){puts("NO");return 0;} v[tmp]=v[a[tmp]]=1; for(int i=1;i<=n;i++) { if(!v[i]) { int top=0; while(!v[i]) { sta[++top]=i; v[i]=1; i=a[i]; } if(top&1){puts("NO");return 0;} for(int j=1;j<=top;j++) { if(j&1)tag[sta[j]]=1; else tag[sta[j]]=2; } } } puts("YES"); printf("%d %d\n",tmp,a[tmp]); for(int i=1;i<=n;i++) { if(tag[i]==1)printf("%d %d\n",tmp,i); else if(tag[i]==2)printf("%d %d\n",a[tmp],i); } }
C. Points on Plane
题意:左下角为[0,0][0,0],右上角为[106,106][10^6,10^6]的矩阵中有nn个点。
询问
求一个nn个点的序列PP
定义dis(a,b)dis(a,b)为点aa与点bb的曼哈顿距离
且∑n−1i=1dis(Pi,Pi+1)<=25∗108\sum_{i=1}^{n-1}{dis(Pi,Pi+1)}<=25*10^8
(md点少或者密集的时候不就是随便输出一个排列么2333)
解析
这个25∗10825*10^8是有内涵的!
我们把这个矩阵切成1000个如下的小矩阵。
然后我们将这些小矩阵按照顺序编上号。
所有奇数号内部的点按照纵坐标升序,偶数号内部的点按照纵坐标降序。
之后我们来考虑这种做法的最糟总和。
显然最糟跨越x是2∗1000∗1000=2∗1062*1000*1000=2*10^6再多一些来回弹的可能性?所以应该比这个要大一两个数量级。
跨越y最糟是每次内部10910^9,穿越10910^9
所以大概最糟可以达到22∗10822*10^8,完全满足题中要求,我们只需要把点按照这种方式排序输出即可。
代码:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define N 1000100 using namespace std; int n; struct Point { int x,y,no; friend istream& operator >> (istream &_,Point &a) {scanf("%d%d",&a.x,&a.y);return _;} }pt ; int get_dis(Point a,Point b) { return abs(a.x-b.x)+abs(a.y-b.y); } bool cmp(Point a,Point b) { return a.x<b.x; } bool cmp2(Point a,Point b) { return a.y<b.y; } bool cmp3(Point a,Point b) { return a.y>b.y; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++){cin>>pt[i];pt[i].no=i;} sort(pt+1,pt+n+1,cmp); int la=0,flag=1,lano=0; for(int i=1;i<=n;i++) { if(pt[i].x-la>=1000) { if(flag) sort(pt+lano+1,pt+i+1,cmp2); else sort(pt+lano+1,pt+i+1,cmp3); lano=i; flag^=1; la+=1000; } } for(int i=1;i<=n;i++)printf("%d ",pt[i].no); puts(""); }
D. Flights for Regular Customers
题意:nn个点,mm条有向边,每条边有3个信息,起点,终点以及一个最小经过等级d。
我们能经过一条边的先决条件是我们之前已经走过了至少d条边。
2 ≤ n ≤ 150,1 ≤ m ≤ 150,0 ≤ di ≤ 1092 ≤ n ≤ 150,1 ≤ m ≤ 150,0 ≤ di ≤ 10^9
询问:
1到n最少经过多少条边。
解析:
由于d很大,所以我们不能用?分层图?的想法强行搞过去。
但是我们观察到m的范围相当的小,也就是说,我们的对应的连边邻接矩阵最多也就是150个。
所以我们考虑是否可以按照边的di值升序排序,之后每一次转移一次disdis矩阵?
发现这种思路似乎可行。
于是我们设dis[i][j]dis[i][j]表示i到j最少经过多少条边。
如果当前有一个01矩阵代表走did_i条边的连通性情况。
然后我们扫到第i+1i+1条边的时候。
显然是走一次的矩阵的(di+1−did_{i+1}-d_i)次幂乘以我们走did_i条边的连通性即可。(注意弄完之后连通性矩阵仍然是01的:D),至于disdis,我们可以在枚举m的情况下用floydfloyd更新即可。
所以复杂度大概是O(n2∗m∗log2d)O(n^2*m*log_2d),出题人说过不了
难道前功尽弃了嘛?(但是我感觉能过,出题人好像算错复杂度了)
并没有,因为这个矩阵始终是一个01矩阵,所以可以上bitset优化嘛23333
复杂度直接变成O(n^2*m*log_2d/32)
代码:
#include <cstdio> #include <bitset> #include <cstring> #include <iostream> #include <algorithm> #define N 160 using namespace std; bitset<N>use ,base ,tmp ; int dis ; int n,m; struct Line { int x,y,d; }edge ; bool cmp(Line a,Line b) { return a.d<b.d; } void mul(bitset<N>*a,bitset<N>*b) { bitset<N>ret ; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j])ret[i]|=b[j]; for(int i=1;i<=n;i++)a[i]=ret[i]; } void Quick_Power(bitset<N>*a,int b) { bitset<N>ret ; for(int i=1;i<=n;i++)ret[i][i]=1; while(b) { if(b&1)mul(ret,a); mul(a,a); b>>=1; } for(int i=1;i<=n;i++)a[i]=ret[i]; } int ans; int main() { ans=0x3f3f3f3f; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,d; scanf("%d%d%d",&x,&y,&d); edge[i].x=x,edge[i].y=y,edge[i].d=d; } sort(edge+1,edge+m+1,cmp); memset(dis,0x3f,sizeof(dis)); for(int i=1;i<=n;i++)use[i][i]=1,dis[i][i]=0; int la=0; for(int i=1;i<=m;i++) { int x=edge[i].x,y=edge[i].y,d=edge[i].d; for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) dis[j][k]=min(dis[j][x]+1+dis[y][k],dis[j][k]); for(int j=1;j<=n;j++)tmp[j]=base[j]; Quick_Power(tmp,d-la); mul(use,tmp); for(int j=1;j<n;j++) { if(use[1][j]) { if(dis[j] !=0x3f3f3f3f) ans=min(ans,d+dis[j] ); } } la=d; base[x][y]=1; } if(ans==0x3f3f3f3f)puts("Impossible"); else printf("%d\n",ans); }
E
去看PoPoQQQ题解吧,bzoj4025好像是这个的弱化,然而我是用lct强行搞过去的。相关文章推荐
- 合并分支
- 网络协议
- linux下编译安装ffmpeg
- 图片始终相对于外层DIV居中
- linux下分卷压缩,合并解压的3种方法
- 树形图计数 count题解
- 细数十大你不得不用的MySQL开发工具(1)
- PHP安装扩展mcrypt以及相关依赖项 【PHP安装PECL扩展的方法】
- ios9学习系列:UIStackView
- Java NIO
- O2O的六种死法
- android源码开发之监听来电状态
- 洗牌与排序
- transitionFromViewController方法的使用--优化
- ServletContextListener监听+Thread使用
- 【烙铁使用规范】—— 延长无铅手工焊接工艺中烙铁头的寿命
- 省市二级联动
- 应用的代码没有用新的类库来进行编译(转)
- 在 Visual Studio 2013 中使用 Grunt, Bower 和 NPM
- iOS开发-文件管理