BestCoder Round #74
来源:互联网 发布:红色警戒mac迅雷下载 编辑:程序博客网 时间:2024/06/07 14:56
A题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5635
官方题解:
如果ai=x, 那么可以推断出si=si+1=...=si+x, 并且如果ai≠0, 那么ai+1=ai−1, 利用第二个条件判断无解, 利用第一个条件划分等价类. 假设有m个等价类, 那么答案就是26⋅25m−1
我的思考:
简单模拟题,还是容易考虑不全面,首先最长公共前缀的长度不能大于后面的最大长度,然后a[i]!=0时a[i+1]必然等于a[i]
#include<cstdio>#include<cstring>using namespace std;#define LL __int64int T,i,n,a[100005];LL ans;const int mod=1e9+7;int main(){ scanf("%d",&T); a[1]=0; while(T--) { scanf("%d",&n); ans=26; for(i=2;i<=n;i++) scanf("%d",&a[i]); for(i=2;i<=n;i++) { if(n-i+1<a[i]||(a[i-1]!=0&&a[i]!=a[i-1]-1)) { ans=0; break; } if(a[i]==0) ans=(ans*25)%mod; } printf("%I64d\n",ans); } return 0;}
B题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5636
官方题解
你可以选择分类讨论, 但是估计可能会写漏一些地方. 只要抽出新增边的端点作为关键点, 建立一个新图, 然后跑一遍floyd就好了. 复杂度大概O(62⋅m)
我的思考:
由于只是新加三个点,所以对于每一个询问,可以暴力枚举所有可能的捷径组合,之前在比赛的时候只考虑到经过一次捷径,没有考虑到可以多条捷径组合 too young to naive
#include<cstdio>#include<cstring>#include<cmath>#define LL __int64using namespace std;const int MOD=1e9+7;int t,f[6],a[6];LL ans,cnt;LL min(LL x,LL y){ return x>y?y:x;}void dfs(int s,int dist){ ans=min(ans,abs(s-t)+dist); for(int i=0;i<6;i++) { if(f[i]) { f[i]=f[i^1]=0; if(dist+abs(s-a[i])+1<ans) dfs(a[i^1],dist+abs(s-a[i])+1); f[i]=f[i^1]=1; } }}int main(){ int T,i,n,m,s; int cnt; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(i=0;i<6;i++) scanf("%d",&a[i]),f[i]=1; cnt=0; for(i=1;i<=m;i++) { scanf("%d%d",&s,&t); ans=MOD; dfs(s,0); cnt=(cnt+i*ans)%MOD; } printf("%d\n",cnt); } return 0;}
官方题解:
注意到答案实际上只和s⊕t有关, bfs预处理下从0到x的最短步数, 然后查询O(1)回答即可.
我的思考:
正如官方题解所说 s^a^b^c……=t 等价于 a^b^c……=s^t
所以原题目就转换乘按题目所给条件从0转化成s^t最短需要几步,预处理情况,之后的询问只要查询数组就可以了
#include<cstdio>#include<queue>#include<cstring>using namespace std;const int mod=1e9+7;const int maxn=1e5+4e4;int T,n,m,ans;int a[20],f[maxn],x,y;int main(){ scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%d",&a[i]); memset(f,1,sizeof(f)); queue<int>q; q.push(0); f[0]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=0;i<n;i++) { if(f[u^a[i]]>f[u]+1) { f[u^a[i]]=f[u]+1; q.push(u^a[i]); } } for(int i=0;i<17;i++) { if(f[u^(1<<i)]>f[u]+1) { f[u^(1<<i)]=f[u]+1; q.push(u^(1<<i)); } } } int res=0; for(int k=1;k<=m;k++) { scanf("%d%d",&x,&y); (res+=k*f[x^y])%=mod; } printf("%d\n",res); } return 0;}
D题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5638
官方题解:
参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于k的所有点, 每次找出编号最小的, 并相应的减少k即可.
这个数据结构可以用线段树, 建立一个线段树每个节点[l,r]维护编号从l到r的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的x满足入度小于等于k.
复杂度O((n+m)logn)
我的思考:
可以用优先队列来做,对于拓扑序列,只要入度为0,那么就可以作为当前的根节点加入序列当中,所以我们可以先求出所有点的入度,按编号升序加入优先队列,我们知道要想使得字典序最小,我们要不惜一切代价使得当前的每一个位置,能取到尽可能小的点,而完全不用顾忌我们要消耗多少的机会来达到这一目的
#include<cstdio>#include<queue>#include<cstring>#include<vector>using namespace std;typedef long long ll;const ll mod=1e9+7;const int maxn=1e5+5;const int maxm=2e5+5;int in[maxn];vector<int>a[maxn];bool vis[maxn];typedef pair<int,int> p;int main(){ int T; scanf("%d",&T); while(T--) { int n,m,k; scanf("%d%d%d",&n,&m,&k); memset(in,0,sizeof(in)); for(int i=0;i<=n;i++)a[i].clear(); while(m--) { int x,y; scanf("%d%d",&x,&y); a[x].push_back(y); in[y]++; } priority_queue<int,vector<int>,greater<int> >q; for(int i=1;i<=n;i++){ q.push(i); } ll ans=0,cnt=1; memset(vis,false,sizeof(vis)); while(!q.empty()) { int temp=q.top(); q.pop(); if(k<in[temp])continue; if(vis[temp])continue; k-=in[temp]; vis[temp]=true; ans+=((temp*cnt)%mod);ans%=mod; cnt++; int tx=temp; int num=a[tx].size(); for(int i=0;i<num;i++){ in[a[tx][i]]--; if(!vis[a[tx][i]]) q.push(a[tx][i]); } } printf("%I64d\n",ans); } return 0;}E题 链接:http://acm.hdu.edu.cn/showproblem.php?pid=5639
官方题解:
方法一:
考虑删掉的边的形态, 就是我们经常见到的环套树这种结构, 参考平时这种图给出的方法, 如果一个图的每个点的出边只有一条, 那么一定会构成环套树这种结构. 于是问题可以转化成, 给无向图的每条边定向, 使得出度最大点的出度最小 (每个点的出度大小对应了删的次数).
显然, 这个东西使可以二分的, 不妨设二分值为x. 考虑混合图的欧拉回路的做法, 利用网络流判合法. 先给每条无向边随便定向, 对于出度大于x的, 从源点连一条流量为deg−x的边, 对于出度小于x的, 从这个点连一条流量为x−deg的边到汇点. 对于原来图中的边, 流量为1加到网络流图中. 只要满流就是合法.
方法二:
类似方法一, 要求的无非是每条边的归属问题, 对于每条边(a,b), 它可以属于a或者b, 那么新建一个节点表示这条边并和a,b都相邻, 这样就得到了一个二分图. 左边是原图中的节点, 右边是原图中的边. 二分每个左边每个节点的容量k, 如果右边的点能够完全匹配, 那么这个k就是可行的, 找到最小的k即可. 转化成二分图多重匹配问题.
方法三:
事实上这题存在O(nm)的做法, 只要在方法二的基础上继续改进就好了, 二分是没有必要的. 注意到每次增广的时候, 增光路中只有端点的容量会变化, 增广路中间的点的容量都不会变化. 那么之要每次增广到端点容量最小的那个点就好了.
我的思考:设经过k次 我们完成了所有的删边操作,所以我们二分k
把所有无向边转换成有向边
我采取的是方法2 新增除了原先的n个节点之外 把每条边看成是新的节点 也就是说新增m个节点,然后跑一下最大流
建图
1.s向所有m个边节点 连一条容量为1的边
2.每个边结点 向他的顶点的节点 连一条容量为1的边
3.每个顶点节点向t连一条容量为k的边,其中k是我们当前二分的答案,如果最大流==m 那么当前是可行方案
然后考虑为什么这样建边能满足题目所给的条件呢,题目要求删边,但每次删的边中不能存在两个环,这个在最大流计算中是这样体现的,我们进行k次删边,然后如果删的边中存在一个环 那么这样进行的一次删边会占据所有删的边的节点到t的边的流量1,然后如果删一个存在两个环的图的话,因为我们知道两个环的图 如果其中有n条边,那么他的节点只有n-1个,也就是说会有一个顶点节点到t的边容量会是2,2的意义也就是说要进行2次删边,所以原问题就完全转换成了上述网络流过程
#include<cstdio>#include<cstring>#include<queue>using namespace std;const int N=2e3+10;int T,n,m,x[N],y[N];struct Maxflow{ const static int maxe=2e6+10; const static int maxp=1e5+10; const static int INF=0x7ffffff; struct node { int x,f; node(){} node(int x,int f):x(x),f(f){}; }edge[maxe]; int head[maxp],next[maxe],dist[maxp],tot,work[maxp],n; void clear(int x){n=x;tot=0;for(int i=0;i<=n;i++) head[i]=-1;} void addedge(int s,int t,int f) { edge[tot]=node(t,0);next[tot]=head[s];head[s]=tot++; edge[tot]=node(s,f);next[tot]=head[t];head[t]=tot++; } bool bfs(int s,int t) { for (int i=0;i<=n;i++) dist[i] = -1; queue<int>p; p.push(s); dist[s]=0; while(!p.empty()) { int q=p.front();p.pop(); for(int i=head[q];i!=-1;i=next[i]) { if (edge[i^1].f&&dist[edge[i].x] ==-1) { p.push(edge[i].x); dist[edge[i].x]=dist[q]+1; if(dist[t]!=-1) return true; } } } return false; } int dfs(int s,int t,int low) { if(s==t)return low; for (int &i=work[s],x;i>=0;i=next[i]) { if(dist[s]+1==dist[edge[i].x]&&edge[i^1].f&&(x=dfs(edge[i].x,t,min(low,edge[i^1].f)))) { edge[i].f+= x; edge[i^1].f-=x; return x; } } return 0; } int dinic(int s,int t) { int maxflow=0,inc=0; while (bfs(s,t)) { for(int i=0;i<=n;i++) work[i]=head[i]; while(inc=dfs(s,t,INF)) maxflow+=inc; } return maxflow; }}solve;bool check(int flow){ solve.clear(n+m+1); for(int i=1;i<=m;i++) { solve.addedge(i,0,1); solve.addedge(m+x[i],i,1); solve.addedge(m+y[i],i,1); } for(int i=m+1;i<=m+n;i++) solve.addedge(n+m+1,i,flow); return solve.dinic(n+m+1,0)==m;}int main(){ scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]); if(m==0) {printf("0\n");continue;} int left=1,right=m; while(left<=right) { int mid=(left+right)/2; if(check(mid)) right=mid-1;else left=mid+1; } printf("%d\n",left); } return 0;}
- BestCoder Round #74
- BestCoder Round #74
- BestCoder Round #74
- BestCoder Round #74 (div.2)
- BestCoder Round #74 Shortest Path
- hdu 5637 BestCoder Round #74 (div.2)
- hdu5635 BestCoder Round #74 (div.2)
- BestCoder Round #74 (div.2) LCP Array
- BestCoder Round #74 HDU 5638 Topsort
- BestCoder Round #74 (div.2)【1排列组合】
- BestCoder Round #3 BestCoder Sequence
- bestcoder round #1
- BestCoder round #1
- BestCoder Round #1
- 【索引】BestCoder Round #2
- BestCoder Round #2
- 【索引】BestCoder Round #3
- BestCoder Round #3
- 置位比特位置查找
- bzoj 2049: [Sdoi2008]Cave 洞穴勘测(LCT模板)
- Java多线程Runnable接口与Thread类,以及synchronied关键字
- shell 之 bash 与sh 区别
- java集合(一)
- BestCoder Round #74
- 错误与断言
- 2016校招阿里电面题【实习】
- java虚拟机 内存模型
- dfs模板
- Linux学习笔记7
- RT3070驱动移植及测试
- oracle11g 在112G内存中如何使用hugepage提高性能
- eclipse validating 很慢