Codeforces #286 Div 1 简要题解
来源:互联网 发布:51单片机控制wifi模块 编辑:程序博客网 时间:2024/05/17 08:53
A. Mr. Kitayuta, the Treasure Hunter
题目链接
http://codeforces.com/contest/506/problem/A
题目大意
有
思路
很显然我们可以得到一个DP的思路:
用
在DP过程中,我们实时维护最终的答案,也就是
但是j的范围可能非常大,因此无论是在空间还是时间上,上述的做法显然是不可行的。
但是考虑最极端的情况:
在最极端的
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXN 40000using namespace std;int f[MAXN][700],n,d;int num[MAXN]; //num[i]=第i个岛屿上的gem个数int main(){ while(scanf("%d%d",&n,&d)!=EOF) { memset(num,0,sizeof(num)); memset(f,-0x3f,sizeof(f)); for(int i=1;i<=n;i++) { int x; scanf("%d",&x); num[x]++; } int ans=num[d]; f[d][300]=num[d]; for(int i=d+1;i<=30000;i++) for(int j=1;j<=600;j++) { int len=j+d-300; //上一步的跳跃距离 if(i-len<0) continue; if(len<=0) continue; f[i][j]=max(f[i][j],f[i-len][j]); f[i][j]=max(f[i][j],f[i-len][j-1]); f[i][j]=max(f[i][j],f[i-len][j+1]); if(f[i][j]!=-0x3f3f3f3f) { f[i][j]+=num[i]; ans=max(ans,f[i][j]); } } printf("%d\n",ans); } return 0;}
B. Mr. Kitayuta’s Technology
题目链接
http://codeforces.com/contest/506/problem/B
题目大意
给出
思路
首先把所有的要求看成有向边
因此我们首先通过DFS/BFS找出所有的弱连通分量,然后对于每个弱连通分量做拓扑排序判环即可得到答案。
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#define MAXE 110000#define MAXV 110000using namespace std;int n,m;struct edge{ int u,v,next;}edges_undir[MAXE*2],edges_dir[MAXE*2];int head_undir[MAXV],head_dir[MAXV],nCount_undir=0,nCount_dir=0;int inDegree[MAXV];void Add_undir(int U,int V){ edges_undir[++nCount_undir].u=U; edges_undir[nCount_undir].v=V; edges_undir[nCount_undir].next=head_undir[U]; head_undir[U]=nCount_undir;}void AddEdge_undir(int U,int V){ Add_undir(U,V); Add_undir(V,U);}void AddEdge_dir(int U,int V){ edges_dir[++nCount_dir].u=U; edges_dir[nCount_dir].v=V; edges_dir[nCount_dir].next=head_dir[U]; head_dir[U]=nCount_dir; inDegree[V]++;}bool vis[MAXV];int stack[MAXV],top=0;void FindWCC(int u,int fa){ vis[u]=true; stack[++top]=u; for(int p=head_undir[u];p!=-1;p=edges_undir[p].next) { int v=edges_undir[p].v; if(v==fa) continue; if(vis[v]) continue; FindWCC(v,u); }}int q[MAXV];bool FindCir(){ int tot=0; //被排序过的点的个数 int h=0,t=0; for(int i=1;i<=top;i++) if(!inDegree[stack[i]]) { q[t++]=stack[i]; tot++; } while(h<t) { int u=q[h++]; for(int p=head_dir[u];p!=-1;p=edges_dir[p].next) { int v=edges_dir[p].v; inDegree[v]--; if(!inDegree[v]) { tot++; q[t++]=v; } } } return tot!=top;}int main(){ memset(head_dir,-1,sizeof(head_dir)); memset(head_undir,-1,sizeof(head_undir)); int ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); AddEdge_dir(u,v); AddEdge_undir(u,v); } for(int i=1;i<=n;i++) { if(vis[i]) continue; top=0; FindWCC(i,-1); if(FindCir()) ans+=top; else ans+=top-1; } printf("%d\n",ans); return 0;}
C. Mr. Kitayuta vs. Bamboos
题目链接
http://codeforces.com/contest/506/problem/C
题目大意
给你
思路
显然直接求值比较困难,需要二分答案
证明倒着推的正确性:只有在
可以发现如果每天对这个竹子的操作相同的话,要想让最后的这个竹子的高度和倒着推的高度一样,之前每天正着推的高度都必须小于等于倒着推的高度。
下面我们就需要通过倒着贪心来判断结果。首先我们尽量少砍伐(倒着推应该叫拔高?)竹子,保证每根竹子倒回来的初始高度大于等于0,且每天每根竹子的高度均大于等于0,我们可以维护一个小根堆,堆里保存的是每根竹子的高度在自然生长下需要多长时间就会变成负数。每天我们取堆里前
现在我们让所有竹子倒过来生长,最终每个竹子高度均大于等于0后,就需要让每个竹子的高度拔高,使得它们最终高度大于等于
这种做法非常巧妙,但是只适用于判定性问题,并不能直接求值
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#include <queue>#define MAXN 110000using namespace std;typedef long long int LL;typedef pair<int,int> pr;int n,m,K;LL P,h[MAXN],a[MAXN];LL nowh[MAXN]; //二分判定时倒着模拟的每根竹子的高度priority_queue<pr,vector<pr>,greater<pr> >heap;bool check(LL lastMaxH){ while(!heap.empty()) heap.pop(); //!!!! for(int i=1;i<=n;i++) nowh[i]=lastMaxH; for(int i=1;i<=n;i++) { if(nowh[i]-m*a[i]>=0) continue; heap.push(make_pair(nowh[i]/a[i]-1,i)); } int tot=0; //最后tot=使得所有竹子的高度大于等于初始高度的最少进行的砍伐次数 for(;tot<=m*K;tot++) //进行tot次砍竹(倒着处理就是增加高度)操作,第tot次操作发生在tot/K天,这个循环后使得每个竹子在开始的那天高度不为负数 { if(heap.empty()) break; pr now=heap.top(); heap.pop(); if(now.first<tot/K) return false; nowh[now.second]+=P; if(nowh[now.second]-m*a[now.second]>=0) continue; heap.push(make_pair(nowh[now.second]/a[now.second]-1,now.second)); } if(tot>m*K) return false; for(int i=1;i<=n;i++) { if(nowh[i]-m*a[i]>=h[i]) continue; tot+=((h[i]-nowh[i]+m*a[i])+P-1)/P; //加上一定操作次数,使得第i个竹子刚刚好比它的初始高度h[i]高 if(tot>m*K) return false; } return true;}int main(){ scanf("%d%d%d%I64d",&n,&m,&K,&P); for(int i=1;i<=n;i++) scanf("%I64d%I64d",&h[i],&a[i]); LL lowerBound=0,upperBound=1e15,ans=-1; while(lowerBound<=upperBound) { LL mid=(lowerBound+upperBound)>>1; if(check(mid)) { ans=mid; upperBound=mid-1; } else lowerBound=mid+1; } printf("%I64d\n",ans); return 0;}
D. Mr. Kitayuta’s Colorful Graph
题目链接
http://codeforces.com/contest/506/problem/D
题目大意
给你一个
思路
很显然,若对于
因此我们可以先对所有边按照颜色进行离线排序,对于每种颜色的无向边分开考虑,用并查集维护只含该种颜色的边的无向图的连通性,然后离线回答所有的询问的答案,看有多少个询问的
问题是离线回答答案时,若是扫描询问来回答答案,
当然还有地方需要优化:每次做并查集数组和其他数组初始化时,我们只初始化那些被修改过的地方,数组里的其他地方不需要优化,不然前面的优化就白做了,还是会TLE
代码
#include <iostream>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <algorithm>#include <map>#include <vector>#define MAXV 110000#define MAXE 110000using namespace std;map<pair<int,int>,int>mp;vector<int>vec[MAXV],block;bool inBlock[MAXV];int n,m,q;struct edge{ int u,v,col;}edges[MAXE];bool cmp(edge a,edge b){ return a.col<b.col;}int f[MAXV];int findSet(int x){ if(f[x]==x) return x; return f[x]=findSet(f[x]);}void set_init(){ for(int i=0;i<block.size();i++) { f[block[i]]=block[i]; inBlock[block[i]]=false; }}struct Query{ int x,y;}query[MAXV];int L,R;void calc(){ if(block.size()>100) //扫一遍所有的点,并遍历每个点a对应的询问里的另一个点b { for(int i=0;i<block.size();i++) { int u=block[i]; for(int j=0;j<vec[u].size();j++) { int v=vec[u][j]; int rootu=findSet(u),rootv=findSet(v); if(rootu!=rootv) continue; mp[make_pair(u,v)]++; } } } else //暴力枚举点对 { for(int i=0;i<block.size();i++) { int u=block[i]; for(int j=i-1;j>=0;j--) { int v=block[j]; int rootu=findSet(u),rootv=findSet(v); if(rootu!=rootv) continue; mp[make_pair(min(u,v),max(u,v))]++; } } }}int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&edges[i].u,&edges[i].v,&edges[i].col); sort(edges+1,edges+m+1,cmp); scanf("%d",&q); for(int i=1;i<=q;i++) { int u,v; scanf("%d%d",&u,&v); if(u>v) swap(u,v); query[i].x=u,query[i].y=v; if(mp.find(make_pair(u,v))==mp.end()) { mp[make_pair(u,v)]=0; vec[u].push_back(v); vec[v].push_back(u); } } for(int i=0;i<MAXV;i++) f[i]=i; for(int nowcol=edges[1].col,i=1;i<=m;i++) { if(edges[i].col==nowcol) { int rootu=findSet(edges[i].u),rootv=findSet(edges[i].v); if(rootu!=rootv) f[rootu]=rootv; if(!inBlock[edges[i].u]) { inBlock[edges[i].u]=true; block.push_back(edges[i].u); } if(!inBlock[edges[i].v]) { inBlock[edges[i].v]=true; block.push_back(edges[i].v); } } else { calc(); set_init(); nowcol=edges[i].col; block.clear(); //memset(inBlock,false,sizeof(inBlock)); int rootu=findSet(edges[i].u),rootv=findSet(edges[i].v); if(rootu!=rootv) f[rootu]=rootv; if(!inBlock[edges[i].u]) { inBlock[edges[i].u]=true; block.push_back(edges[i].u); } if(!inBlock[edges[i].v]) { inBlock[edges[i].v]=true; block.push_back(edges[i].v); } } } if(!block.empty()) calc(); for(int i=1;i<=q;i++) printf("%d\n",mp[make_pair(query[i].x,query[i].y)]); return 0;}
- Codeforces #286 Div 1 简要题解
- [Codeforces #295(Div 1)]简要题解
- Codeforces #299 Div 1 简要题解
- Codeforces #292 Div 1 简要题解
- Codeforces #305 Div 1 简要题解
- Codeforces #290 Div. 1 简要题解
- Codeforces #285 Div 1 简要题解
- Codeforces #284 Div 1 简要题解
- Codeforces #283 Div 1 简要题解
- Codeforces #278 Div 1 简要题解
- Codeforces #275 Div 1 简要题解
- Codeforces #274 Div 1 简要题解
- Codeforces #272 Div 1 简要题解
- Codeforces #268 Div 1 简要题解
- Codeforces #265 Div 1 简要题解
- Codeforces #263 Div 1 简要题解
- Codeforces #310 Div 1 简要题解
- Codeforces #309 Div 1 简要题解
- OpenCV中的模板匹配方法及其应用
- 用Python和Pygame写游戏-从入门到精通(6)
- 单精度浮点数按存储格式转为整数的程序
- 计算机科学中最重要的32个算法
- fnlp实践——新闻关键词提取
- Codeforces #286 Div 1 简要题解
- 用Python和Pygame写游戏-从入门到精通(7)
- 项目管理修炼之道(三 使用生命周期组织项目)
- 面试题:switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
- 简述三个范式的含义
- HDU - 3658 How many words 矩阵快速幂
- 用Python和Pygame写游戏-从入门到精通(8)
- 我为虎嗅设计APP(一)-逻辑梳理
- Python 的 Socket 编程教程