AtCoder Regular 80
来源:互联网 发布:李小冉 知乎 编辑:程序博客网 时间:2024/05/20 09:26
AtCoder Regular Contest 080
被F题卡了,现在才懂…
目录
- AtCoder Regular Contest 080
- C 4-adjacent
- 思路
- 代码
- D Grid Coloring
- 思路
- 代码
- E Young Maids
- 思路
- 代码
- F Prime Flip
- 思路
- 代码
C. 4-adjacent
题目大意:给你一个数列
a ,问是否能通过置换(交换某些元素的位置),使得该数列满足ai∗ai+1
思路
- 显然要将4的倍数和奇数交叉放,2的倍数放一起,那么只需统计一下这三种数的个数算一下就好了,很简单,注意一下细节就好(样例很良心,过了一般都A了)。
代码
#include <cstdio>const int MAXN=1e5+5;int n,cnt_4,cnt_not4;int a[MAXN];int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); if(a[i]%4==0) ++cnt_4; else if(a[i]%2) ++cnt_not4; } if(cnt_4>=cnt_not4 || (cnt_4>=cnt_not4-1 && cnt_4+cnt_not4==n)) printf("Yes"); else printf("No"); return 0;}
D. Grid Coloring
题目大意:给你
n 种颜色,每种有一个数量要求ai ,你要对一个H∗W 的四连通矩阵染色,使每种颜色数量为ai ,并且每种颜色的方格必须互相连通,给出一种方案。
思路
- 直接蛇形染色就好了,很水。。。
代码
#include <cstdio>#include <queue>using std::queue;const int MAXHW=105;int n,h,w,a;int sqr[MAXHW][MAXHW];queue<int> q,ans;int main(){ scanf("%d%d%d",&h,&w,&n); for(int i=1;i<=n;++i){ scanf("%d",&a); q.push(a); } int k=0; while(++k,q.size()){ a=q.front(); q.pop(); for(int i=1;i<=a;++i) ans.push(k); } for(int i=1;i<=h;i+=2){ for(int j=1;j<=w;++j){ sqr[i][j]=ans.front(); ans.pop(); } for(int j=w;j && ans.size();--j){ sqr[i+1][j]=ans.front(); ans.pop(); } } for(int i=1;i<=h;++i){ for(int j=1;j<=w;++j) printf("%d ",sqr[i][j]); putchar('\n'); } return 0;}
E. Young Maids
题目大意:给你一个数列,每次可以取出相邻的两个数按原数列顺序加到一个新的数列的头部,求字典序最小的新数列。
思路
考试的时候傻掉了...
- 求字典序最小的这种显然贪心;
- 因为是不断向新数列头部添加,我们就倒着来,每次取原数列最小且合法的两个加在新数列尾部;
- 现在考虑怎么保证合法:
- 首先,第一次应该从奇数位置挑一个最小的并在其后面的偶数位置挑一个最小的;
- 之后,我们可以把原数列分成三段(有的段可能是空的),这样在每段内先挑和该段左端点位置奇偶性相同的,再在其后面且在该段内、位置的奇偶性不同的里面挑一个,操作总是合法的;
- 所以我们用堆维护一下这些区间,按区间内最小元素排序,每次取堆顶的区间进行以上操作,再将新产生的区间加入堆中即可;
- 又因为要查询区间最值,还需要用一个数据结构维护一下;
代码
整体不难,但实现起来有些繁琐
#include <cstdio>#include <algorithm>#include <utility>#include <queue>#include <vector>#include <functional>#include <climits>#include <cmath>using std::min;using std::pair;using std::priority_queue;using std::vector;using std::greater;typedef pair<int,int> range;typedef pair<int,int> number;typedef pair<number,range> sub_qry;const int MAXN=2e5+5,MAXLOG=(int)(log(MAXN)/log(2)+0.5);int p[MAXN];class SparseTab{ private: int log_tab[MAXN]; number c[MAXLOG][MAXN]; public: void build(int n,int a[],bool type){ log_tab[0]=-1; for(int i=1;i<=n;++i){ c[0][i]=number((i&1)==type ? a[i]:INT_MAX,i); log_tab[i]= i&(i-1) ? log_tab[i-1]:log_tab[i-1]+1; } for(int i=1;i<=log_tab[n];++i){ for(int j=1;j+(1<<i)-1<=n;++j) c[i][j]=min(c[i-1][j],c[i-1][j+(1<<i-1)]); } } number qry(int l,int r){ int k=log_tab[r-l+1]; return min(c[k][l],c[k][r-(1<<k)+1]); }}odd,even;inline void solve(int n){ static priority_queue<sub_qry,vector<sub_qry>,greater<sub_qry> > hp; number tmp1=odd.qry(1,n),tmp2=even.qry(tmp1.second,n); printf("%d %d ",tmp1.first,tmp2.first); if(1<=tmp1.second-1) hp.push(sub_qry(odd.qry(1,tmp1.second-1),range(1,tmp1.second-1))); if(tmp1.second+1<=tmp2.second-1) hp.push(sub_qry(even.qry(tmp1.second+1,tmp2.second-1),range(tmp1.second+1,tmp2.second-1))); if(tmp2.second+1<=n) hp.push(sub_qry(odd.qry(tmp2.second+1,n),range(tmp2.second+1,n))); sub_qry now; number that; SparseTab *st1,*st2; while(hp.size()){ now=hp.top(); hp.pop(); st1= now.second.first&1 ? &odd:&even; st2= now.second.first&1 ? &even:&odd; that=st2->qry(now.first.second,now.second.second); printf("%d %d ",now.first.first,that.first); if(now.second.first<now.first.second-1) hp.push(sub_qry(st1->qry(now.second.first,now.first.second-1),range(now.second.first,now.first.second-1))); if(now.first.second+1<that.second-1) hp.push(sub_qry(st2->qry(now.first.second+1,that.second-1),range(now.first.second+1,that.second-1))); if(that.second+1<now.second.second) hp.push(sub_qry(st1->qry(that.second+1,now.second.second),range(that.second+1,now.second.second))); }}int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&p[i]); odd.build(n,p,1),even.build(n,p,0); solve(n); return 0;}
F. Prime Flip
题目大意:有一个无限长的卡片序列标号为
1,2,⋯,∞ ,给定n 个数x1,x2,⋯,xn ,这些数对应标号的卡片是向上的,其它为向下的,每次只可以连续翻转奇质数长度的卡片,求使得整个卡片序列都向下的最少操作次数。(1≤n≤100 ,1≤max(xi)≤107 ).
思路
这题太神了,看了半天题解。。。orz
- 考虑将原问题进行转化,记
i 号卡片是否向上为布尔函数is_up(i) 且is_up(0)=0 ,设01序列b 满足bi=(is_up(i)==is_up(i−1)) ,则翻转一段连续的卡片[l,r] 可以表示为bl=!bl,br+1=!br+1 ; - 那么问题就变成了给定一个01序列
b ,每次对两个元素bi,bj 取反,其中|j−i| 是质数,求使得该序列元素全变为0的最少操作次数; 设对
bi,bj 取反的最小代价为cost(i,j) ,其中|j−i| 不一定为质数,现在我们分四种情况讨论:|j−i| 是质数,则cost(i,j)=1 ;|j−i| 是偶数,则根据波利尼亚克猜想,任意一个偶数可以表示为两个质数之差(至少在这题的数据范围内是成立的…),cost(i,j)=2 ;|j−i| 是奇合数,则有cost(i,j)=3 ,因为有一个“猜想”(它貌似不能被完全证明,只能说在这题范围内成立):任意一个奇合数可以表示为3个奇质数之和。证明:
- 根据维诺格拉多夫定理,任意一个大于5的奇数可以表示为3个质数之和。
- 那么只需证明若一个奇合数能被分解为
2+2+x,x 为奇质数时,它一定能表示为其它3个奇质数之和。 - 将该奇合数写成
y+x,y=4 ,考虑当x>3 时,不断将y+=2,x−=2 ,则总有一刻,x 变为一个奇质数(eg.3,5,7,⋯ )。 - 又因为哥德巴赫猜想,偶数
y 定能写成两个质数之和(至少在这题数据范围内可以),并且当y=4−>6 后,y 写成的质数和不含2! - 最后,再考虑
x=3 时,2+2+3==7 ,7不是奇合数,证毕!(出题人太神了orz)。
|j−i|==1 ,考虑一个质数减去它相邻的比它小的质数等于1,则|j−i|==1 的情况可以被分解为|j−i| 是质数和偶数两个情况,所以cost(i,j)=3 .
- 又由于每一段连续向上的卡片,都产生了两个1,我们只需看怎么将这些1两两分组使
∑cost(i,j) 最小; - 现在,我们的任务是将这些1两两配对,每对的代价为
cost(i,j) ,求最小代价和; - 将1按原位置奇偶分组,则每组里的1两两配对,它们的
|j−i| 定为偶数,而两组间配对,|j−i| 为奇数,有两种可能; - 由于,我们要求最小代价,我们先对这两组1求二分图最大匹配(只连
|j−i| 为质数的边),设匹配了k 对,则其花费为k ,剩下的我们先试图用代价为2的来配对,最后再用3; - 设奇数组1的个数为odd_size,偶数组1的个数为even_size,那么
ans=k+(odd_size/2+even_size/2)∗2+odd_size .
代码
#include <cstdio>#include <cmath>#include <climits>#include <queue>#include <algorithm>using std::queue;using std::min;const int MAXX=1e7+5,MAXN=205;int n,cnt_x,cnt_y;int x_id[MAXN],y_id[MAXN];bool up[MAXX];struct node{int he,iter,dis;}d[MAXN];struct line{int to,nex,cap;}ed[MAXN*MAXN<<1];inline bool is_odd_prm(int x){ if(x==1) return false; for(int i=2;i*i<=x;++i){ if(x%i==0) return false; } return true;}inline void addE(int u,int v,int cap){ static int cnt=1; ed[++cnt]=(line){v,d[u].he,cap}; d[u].he=cnt;}inline int revE(int i){return i^1;}inline bool BFS(int s,int t,int n){ for(int i=1;i<=n;++i) d[i].dis=-1; static queue<int> q; d[s].dis=0; q.push(s); int u; while(q.size()){ u=q.front(); q.pop(); for(int i=d[u].he,v;i;i=ed[i].nex){ if(ed[i].cap==0) continue; v=ed[i].to; if(d[v].dis==-1){ d[v].dis=d[u].dis+1; q.push(v); } } } return d[t].dis!=-1;}int aug(int u,int rest,const int t){ if(u==t) return rest; int ret=0; for(int &i=d[u].iter,v,cap,flow;i;i=ed[i].nex){ v=ed[i].to,cap=ed[i].cap; if(d[v].dis!=d[u].dis+1 || cap==0) continue; flow=aug(v,min(cap,rest),t); ed[i].cap-=flow,ed[revE(i)].cap+=flow; ret+=flow,rest-=flow; if(rest==0) return ret; } if(ret==0) d[u].dis=-1; return ret;}inline int Dinic(int s,int t,int n){ int ret=0; while(BFS(s,t,n)){ for(int i=1;i<=n;++i) d[i].iter=d[i].he; ret+=aug(s,INT_MAX,t); } return ret;}inline void build(){ for(int i=1;i<=cnt_x;++i){ for(int j=1,v;j<=cnt_y;++j){ if(is_odd_prm(abs(x_id[i]-y_id[j]))){ v=j+cnt_x; addE(i,v,INT_MAX),addE(v,i,0); } } } for(int i=1,s=cnt_x+cnt_y+1;i<=cnt_x;++i) addE(s,i,1),addE(i,s,0); for(int i=1,u,t=cnt_x+cnt_y+2;i<=cnt_y;++i){ u=cnt_x+i; addE(u,t,1),addE(t,u,0); }}int main(){ scanf("%d",&n); int x; for(int i=1;i<=n;++i){ scanf("%d",&x); up[x]=true; } for(int i=1;i<=x+1;++i){ if(up[i]!=up[i-1]) i&1 ? x_id[++cnt_x]=i:y_id[++cnt_y]=i; } build(); int k=Dinic(cnt_x+cnt_y+1,cnt_x+cnt_y+2,cnt_x+cnt_y+2); printf("%d",k+((cnt_x-k)/2+(cnt_y-k)/2)*2+(cnt_x-k)%2*3); return 0;}
阅读全文
0 0
- AtCoder Regular 80
- AtCoder Regular Contest 077
- AtCoder Regular Contest 077
- AtCoder Regular Contest 078
- AtCoder Regular Contest 079
- Atcoder Regular Contest 084
- AtCoder Regular Contest 086
- AtCoder Regular Contest 068
- AtCoder Regular Contest 088
- atcoder AtCoder Regular Contest 084 D
- AtCoder Regular Contest 063题解
- AtCoder Regular Contest 069 D
- AtCoder Regular Contest 071 F
- AtCoder Regular Contest 073 D
- AtCoder Regular Contest 076 F
- AtCoder Regular Contest 077-C
- AtCoder Regular Contest 077-D
- AtCoder Regular Contest 077 E
- 图论基础算法
- 练习 1
- netty学习八:在window上安装thrift以及第一个小demo
- 迷之阶梯
- Unity手游制作记-制作通用对象管理器(二)
- AtCoder Regular 80
- 黑白子交换
- AIDL的基本使用(一)
- Linux系统的"护花使者"-----守护进程
- View事件的传递
- css3元素向上移动
- R实战:【I/O】文本文件与因子转换
- 最佳调度问题
- VMware12安装Linux7系统进而安装Oracle12c中遇到的问题