【UR #2】跳蚤公路
【UR #2】跳蚤公路
参照yjc方法。也就是地铁环线那个题。
求每个点不在负环内的x的取值范围。然后所有1到j能到i的j的范围取交。得到答案。
每个边形如kx+b的直线,每个环也是
每个点不在负环内的x取值范围是区间,
两次二分,
第一次二分区间左端点,第二次右端点。
如果没有负环,左端点往左偏,右端点往右偏
否则,记录负环的构成:k*mid+b的k的正负,可以得到mid应该往哪里偏。
注意SPFA找负环:
记录has[x]表示到x的最短路已经经过了多少个点,
dis[x]最短路,fr[x]是最短路的前驱,pre[x]是最短路前驱指向x的边
发现has[x]>n的时候,证明出现了负环。但是x不一定在负环上!
不断跳fr[x]找到整个环重复的第一个点z。
再fr[z]找到整个环。
emmm,一个问题是,负环上的点不会被其他点松弛导致fr[*]找不到负环吗?
由于SPFA的BFS性质,以及has[x]>n才会判断出有负环,
所以整个负环上的点,在判断has[*]>n之前,要么不会被松弛、或者松弛后要么找到新的负环、要么会被这个负环再次松弛,
总之这个环确实能找出来。
代码:
目前(2019.6.17)UOJ最优解
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);} template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Modulo{ const int mod=998244353; il int ad(int x,int y){return x+y>=mod?x+y-mod:x+y;} il int sub(int x,int y){return ad(x,mod-y);} il int mul(int x,int y){return (ll)x*y%mod;} il void inc(int &x,int y){x=ad(x,y);} il void inc2(int &x,int y){x=mul(x,y);} il int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;} template<class ...Args>il int ad(const int a,const int b,const Args &...args) {return ad(ad(a,b),args...);} template<class ...Args>il int mul(const int a,const int b,const Args &...args) {return mul(mul(a,b),args...);} } // using namespace Modulo; namespace Miracle{ const int N=105; const int M=10005; const ll inf=1e14; int n,m; struct edge{int x,y,k,b; }b[M]; struct node{int nxt,to;int k,b;ll val(ll x){return k*x+b;} }e[2*M]; int hd[N],cnt; void add(int x,int y,int k,int b){e[++cnt].nxt=hd[x];e[cnt].to=y;e[cnt].k=k;e[cnt].b=b;hd[x]=cnt; } int c[N],df,dfn[N],low[N]; int scc; int f[N][N]; int sta[N],top,in[N];int sz[N]; void tarjan(int x){dfn[x]=low[x]=++df;sta[++top]=x;in[x]=1;for(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(!dfn[y]){tarjan(y);low[x]=min(low[x],low[y]);}else if(in[y]) low[x]=min(low[x],dfn[y]);}if(low[x]==dfn[x]){++scc;int z;do{z=sta[top--];in[z]=0;c[z]=scc;++sz[scc];}while(z!=x);} } struct seg{ll l,r;seg(){l=-inf,r=inf;}seg(ll le,ll ri){l=le;r=ri;}bool empty(){return l>r;}bool full(){return (l==-inf)&&(r==inf);}seg friend operator &(seg a,seg b){return seg(max(a.l,b.l),min(a.r,b.r));}seg friend operator |(seg a,seg b){if(a.empty()) return b;if(b.empty()) return a;return seg(min(a.l,b.l),max(a.r,b.r));} }lim[N]; ll dis[N]; int pre[N]; int fr[N]; int has[N]; queue<int>q; bool vis[N]; int spfa(int s,ll mid,int n){//-1 need small; 1: need big ;0 okwhile(!q.empty()) q.pop();memset(dis,0x3f,sizeof dis);memset(vis,0,sizeof vis);// memset(pre,0,sizeof pre);// memset(has,0,sizeof has); dis[s]=0;has[s]=1;q.push(s);while(!q.empty()){int x=q.front();q.pop();// cout<<" xx "<<x<<endl;vis[x]=0;for(reg i=hd[x];i;i=e[i].nxt){int y=e[i].to;if(dis[y]>dis[x]+e[i].val(mid)){dis[y]=dis[x]+e[i].val(mid);pre[y]=i;fr[y]=x;has[y]=has[x]+1;if(has[y]>n){//has fuhuan // cout<<" fuhuan !!!"<<endl;int z=y;int k=0;memset(vis,0,sizeof vis);do{// cout<<" zz "<<z<<endl;// k+=e[pre[z]].k;vis[z]=1;z=fr[z];}while(!vis[z]);int lp=z;do{k+=e[pre[z]].k;z=fr[z];}while(z!=lp);if(k<0){return -1;}else{return 1;}}if(!vis[y]){vis[y]=1;q.push(y);}}}}return 0; } void calc(int s,int id){// cout<<" calc "<<id<<" s "<<s<<endl;ll al=inf+1;ll L=-inf,R=inf;while(L<=R){ll mid=(L+R)>>1;// cout<<L<<" "<<R<<" : "<<mid<<endl;int lp=spfa(s,mid,sz[id]);if(lp==-1){R=mid-1;}else if(lp==1){L=mid+1;}else{al=mid;R=mid-1;}}// cout<<" al "<<al<<endl;ll ar=-inf-1;L=-inf,R=inf;while(L<=R){ll mid=(L+R)>>1;int lp=spfa(s,mid,sz[id]);if(lp==-1){R=mid-1;}else if(lp==1){L=mid+1;}else{ar=mid;L=mid+1;}}// cout<<" ar "<<ar<<endl;lim[id]=seg(al,ar); } int main(){rd(n);rd(m);for(reg i=1;i<=m;++i){rd(b[i].x);rd(b[i].y);rd(b[i].b);rd(b[i].k);add(b[i].x,b[i].y,233,233);// f[b[i].x][b[i].y]=1; }for(reg i=1;i<=n;++i){if(!dfn[i]){tarjan(i);}}// cout<<" after tarjan "<<endl;for(reg i=1;i<=scc;++i){int s=0;memset(hd,0,sizeof hd);cnt=0;for(reg j=1;j<=m;++j){if(c[b[j].x]==i&&c[b[j].y]==i){s=b[j].x;add(b[j].x,b[j].y,b[j].k,b[j].b);}}if(s) calc(s,i);}for(reg i=1;i<=scc;++i) f[i][i]=1;for(reg i=1;i<=m;++i){f[c[b[i].x]][c[b[i].y]]=1;}for(reg k=1;k<=scc;++k){for(reg i=1;i<=scc;++i){for(reg j=1;j<=scc;++j){f[i][j]|=(f[i][k])&(f[k][j]);}}}for(reg i=1;i<=n;++i){seg ans;for(reg j=1;j<=scc;++j){if(f[c[1]][j]&&f[j][c[i]]){ans=ans&lim[j]; }}if(ans.empty()){puts("0");}else if(ans.l==-inf||ans.r==inf){puts("-1");}else{printf("%lld\n",ans.r-ans.l+1);}}return 0; } } signed main(){Miracle::main();return 0; }/*Author: *Miracle* */
普通二分+判负环
因为整个值域都有单调性。知道不合法往哪里走。
区间二分?+找负环
二分左端点,二分右端点。
麻烦的是,第一次不合法,该往哪里走?(显然之后不合法其实是知道往哪里走的)
因为并没有单调性。
本题提供的思路是,考虑不合法的构成,来限制往哪里走才可能合法。
也就是额外记录一些东西
(好像这个套路暂时只出现于k*mid+b的k正负判断?)
upda:2019.6.21
官方题解:
利用Bellman-ford的思想进行判断负环。
简介Bellman-ford的方法:
不断枚举边数,用所有的边更新点的dis。
设f[i][j]走了i条边,到达j点,最短路
如果存在负环,则一定存在一个负环上的点u,使得f[u][n]<f[u][n-1]
扩展一下
f[i][j][k]表示,走了i条边,到达j点,斜率是k的最小的b值。
找到每个点不在负环的x的取值范围。
min(f[u][n][k1]+k1*x)>=min(f[u][n-1][k2]+k2*x)的x的个数。
枚举每个k1,求f[u][n][k1]+k1*x>=min(f[u][n-1][k2]+k2*x)的x区间,再求并。
转化为:求f[u][n][k1]+k1*x<min(f[u][n-1][k2]+k2*x)的x区间,求并,再求补集。
再枚举k2,解出的所有不等式f[u][n][k1]+k1*x<f[u][n-1][k2]+k2*x求交。
由于最后答案补集一定是[-inf,l],[l,r],[r,inf],空集,的一种。所以过程中求交求并不会有奇怪情况。
求并的时候sort一下就好了。
然后floyd,从1到u到x的u的区间求交。
虽然不是所有的负环上的点都会考虑到,但是每个负环上至少有一个点可以得到限制,使得没有负环。
最后是求交,所以不影响正确性。
转载于:https://www.cnblogs.com/Miracevin/p/11039649.html
【UR #2】跳蚤公路相关推荐
- [总结]2019年9月 OI学习/刷题记录
从现在开始记录一下每天的学习情况.主力LOJ? 2019/9/5 LibreOJ #2543. 「JXOI2018」排序问题 答案显然是\(\frac{(n+m)!}{Cnt_1!Cnt_2!\cdo ...
- UOJ Round #20 T1 A. 【UR #20】跳蚤电话(组合数+树形DP)
UOJ Round #20 T1 A. [UR #20]跳蚤电话 题目大意 给出一棵树,求建出该树的不同操作方案数.建树方式如下:初始 S S S集合只有 1 1 1,操作 1 1 1为取已连的边 x ...
- UOJ#192. 【UR #14】最强跳蚤
题目大意:给定一颗树,每条边有一个权值,求有多少有序点对使得这两点间路径权值乘起来是完全平方数 我们可以给每个素数随机一个权值,然后把每条边的权值分解质因数,把每个素数换成对应的权值然后异或起来,这样 ...
- 5800计算器公路三维全能程序
5800计算器公路三维全能程序.(丢掉图纸轻松测量)说明清晰! 各位,我的9860程序记忆编写完毕,跟这个程序功能差不多,但是更好用,加入了隧道 计算功能,能计算超欠挖,渐变隧道,不限制圆心数目,程序 ...
- gatsby_如何使用Gatsby和Leaflet创建夏季公路旅行地图绘制应用程序
gatsby Get ready for the summer by building your own road trip mapping app with this step-by-step gu ...
- 公路病害检测有了“智慧眼”,思谋AI“助力”广东省高速公路
近日,思谋科技与广东省某高速企业达成合作,智慧交通一体化平台病害检测模块已成熟落地,将使广东省高速公路病害检测进入高频率.高效率.智能化时代,以行业领先的AI技术助力智慧交通产业的发展. 近年来,我国 ...
- 公路修建问题(二分+最小生成树)
Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2061 Solved: 1184 Description OI island是一个非常漂亮的岛屿, ...
- 东北大学 | 一种适用于大规模公路环境的鲁棒激光惯性里程计和建图系统
点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 标题: A Robust Laser-Inertial Odometry and Mapping Me ...
- db2字符串不能累加的吗_二建可以考两种专业吗?建筑和公路能同时考吗?
导语: 2020年二级建造师报考问题的答疑来了. 二级建造师报考 报考二级建造师可以考两种不同的专业吗?比如建筑和公路. [答疑]不能一年同时考,因为各专业考试都是同一时间.但是可以先考一门,然后参加 ...
最新文章
- 服务器Jmail配置问题
- 9.11排序与查找(三)——给定一个排序后的数组,包括n个整数,但这个数组已被旋转过多次,找出数组中的某个元素...
- 手动升级oracle,ORACLE 10.2.0.1手动升级到10.2.0.4
- 澳元兑美元震荡整理,后市可否追高
- 详述怎么使用Linux救援模式
- Example-Based Facial Rigging
- Top3获奖金10万,Seq2seq对话系统设计方案
- jQuery的对象访问函数(get,index,size,each)
- 数字电子技术期末考试思维导图
- 双层PDF—OCR文字识别系统
- The project seems to require yarn but it‘s not installed.
- Perl-LWP文档
- “JEPG”到“TXT”,魔幻的Loot
- ASM磁盘组中的控制文件丢失处理
- HTML5 拖放Drag和drop用法以及事件介绍
- rtos系统c语言,让我们来学习RTOS,自己写RTOS
- 钩陈/ 为什么会忍不住复训蟒营™
- QQ邮箱SMTP限流
- 24c02存储器(iic通信协议)
- [通达OA二次开发]FineReport 与通达OA集成 如何实现统一登录。