【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=;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
for(x=numb;isdigit(ch=getchar());x=x*+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/)output(x/);putchar(x%+'');}
template<class T>il void ot(T x){if(x<) 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=;
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-){int ret=;while(y){if(y&) ret=mul(x,ret);x=mul(x,x);y>>=;}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=;
const int M=;
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[*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]=;
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]=;
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 ok
while(!q.empty()) q.pop();
memset(dis,0x3f,sizeof dis);
memset(vis,,sizeof vis);
// memset(pre,0,sizeof pre);
// memset(has,0,sizeof has); dis[s]=;has[s]=;
q.push(s);
while(!q.empty()){
int x=q.front();q.pop();
// cout<<" xx "<<x<<endl;
vis[x]=; 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]+; if(has[y]>n){//has fuhuan
// cout<<" fuhuan !!!"<<endl;
int z=y;
int k=;
memset(vis,,sizeof vis);
do{
// cout<<" zz "<<z<<endl;
// k+=e[pre[z]].k;
vis[z]=;
z=fr[z];
}while(!vis[z]); int lp=z;
do{
k+=e[pre[z]].k;
z=fr[z];
}while(z!=lp); if(k<){
return -;
}else{
return ;
}
} if(!vis[y]){
vis[y]=;
q.push(y);
}
}
}
}
return ;
}
void calc(int s,int id){
// cout<<" calc "<<id<<" s "<<s<<endl;
ll al=inf+;
ll L=-inf,R=inf;
while(L<=R){
ll mid=(L+R)>>;
// cout<<L<<" "<<R<<" : "<<mid<<endl;
int lp=spfa(s,mid,sz[id]);
if(lp==-){
R=mid-;
}else if(lp==){
L=mid+;
}else{
al=mid;
R=mid-;
}
}
// cout<<" al "<<al<<endl;
ll ar=-inf-;
L=-inf,R=inf;
while(L<=R){
ll mid=(L+R)>>; int lp=spfa(s,mid,sz[id]);
if(lp==-){
R=mid-;
}else if(lp==){
L=mid+;
}else{
ar=mid;
L=mid+;
}
}
// cout<<" ar "<<ar<<endl;
lim[id]=seg(al,ar);
}
int main(){
rd(n);rd(m);
for(reg i=;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,,);
// f[b[i].x][b[i].y]=1;
}
for(reg i=;i<=n;++i){
if(!dfn[i]){
tarjan(i);
}
}
// cout<<" after tarjan "<<endl;
for(reg i=;i<=scc;++i){
int s=;
memset(hd,,sizeof hd);
cnt=;
for(reg j=;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=;i<=scc;++i) f[i][i]=;
for(reg i=;i<=m;++i){
f[c[b[i].x]][c[b[i].y]]=;
}
for(reg k=;k<=scc;++k){
for(reg i=;i<=scc;++i){
for(reg j=;j<=scc;++j){
f[i][j]|=(f[i][k])&(f[k][j]);
}
}
}
for(reg i=;i<=n;++i){
seg ans;
for(reg j=;j<=scc;++j){
if(f[c[]][j]&&f[j][c[i]]){
ans=ans&lim[j];
}
}
if(ans.empty()){
puts("");
}
else if(ans.l==-inf||ans.r==inf){
puts("-1");
}else{
printf("%lld\n",ans.r-ans.l+);
}
}
return ;
} }
signed main(){
Miracle::main();
return ;
} /*
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的区间求交。

虽然不是所有的负环上的点都会考虑到,但是每个负环上至少有一个点可以得到限制,使得没有负环。

最后是求交,所以不影响正确性。

【UR #2】跳蚤公路的更多相关文章

  1. 【UOJ#32】【UR #2】跳蚤公路(最短路)

    [UOJ#32][UR #2]跳蚤公路(最短路) 题面 UOJ 题解 不难发现要求的就是是否存在负环.也就是我们只需要找到所有的负的简单环,很容易就可以想到维护路径上和\(x\)相关的内容,即维护一下 ...

  2. UOJ #32. 【UR #2】跳蚤公路【Floydbellman-ford】

    首先看这个范围很夸张但是其实有限制的也就在1e18*n范围里(走完一圈的边权),然后限制一定是有负环 用Floyd传递闭包,然后设f[i][j][k]为从1走了i步到j并且有k个x的最短路,用B-F处 ...

  3. Solution -「UR #2」「UOJ #32」跳蚤公路

    \(\mathcal{Description}\)   Link.   给定一个 \(n\) 个点 \(m\) 条边的带权有向图,每条边还有属性 \(s\in\{-1,0,1\}\).对于每个 \(u ...

  4. UOJ Round总结

    #22. [UR #1]外星人 一开始随便搞出第一问答案,很显然的性质对$x$有变化的$a$一定是递减的,就拿一个桶直接记录可以达到的值 然后我开始想第二问,一开始想直接在这个桶上统计答案,然后发现不 ...

  5. UOJ #192 【UR #14】 最强跳蚤

    题目链接:最强跳蚤 这道题本来不想写博客的--但是鉴于自己犯了低级错误,还是写篇博客记载一下. 一开始我的想法和题解里面的算法而比较类似,也是先分解质因数,然后用质因子是否出现偶数次来判断当前这个数是 ...

  6. UOJ#23. 【UR #1】跳蚤国王下江南 仙人掌 Tarjan 点双 圆方树 点分治 多项式 FFT

    原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ23.html 题目传送门 - UOJ#23 题意 给定一个有 n 个节点的仙人掌(可能有重边). 对于所有 ...

  7. (GDOI2018模拟九)【UOJ#192】【UR#14】最强跳蚤

    (开头先Orz myh) 原题目: 在人类和跳蚤的战争初期,人们凭借着地理优势占据了上风——即使是最强壮的跳蚤,也无法一下越过那一堵坚固的城墙. 在经历了惨痛的牺牲后,跳蚤国王意识到再这样下去,跳蚤国 ...

  8. UOJ#192. 【UR #14】最强跳蚤

    题目链接 http://uoj.ac/problem/192 暑期课第二天 树上问题进阶 具体内容看笔记博客吧 题意 n个节点的树T 边有边权w 求满足(u, v)上所有边权乘积为完全平方数的路径有多 ...

  9. 【uoj#192】[UR #14]最强跳蚤 Hash

    题目描述 给定一棵 $n$ 个点的树,边有边权.求简单路径上的边的乘积为完全平方数的点对 $(x,y)\ ,\ x\ne y$ 的数目. 题解 Hash 一个数是完全平方数,当且仅当每个质因子出现次数 ...

随机推荐

  1. URAL 1057 Amount of Degrees (数位dp)

    Create a code to determine the amount of integers, lying in the set [X;Y] and being a sum of exactly ...

  2. Service系统服务(三):查看进程信息、进程调度及终止、系统日志分析、使用systemctl工具

    一.查看进程信息 目标: 本例要求掌握查看进程信息的操作,使用必要的命令工具完成下列任务: 找出进程 gdm 的 PID 编号值 列出由进程 gdm 开始的子进程树结构信息 找出进程 sshd 的父进 ...

  3. 团队冲刺DAY5

    团队冲刺DAY5 今天的内容是组件和事件处理这一部分,也就是需要扣一个消息系统的图形界面. 提到这部分,就不得不说Java Swing. 常用组件及部件 JTextField:文本框 JTextAre ...

  4. react jsx 中使用 switch case 示例

    <div> <span>适用平台:</span> <span>{(() => { switch (currentItems.usePlatform ...

  5. 关于UI自动化测试的思考

    不知不觉,时间过去了二年多,从开始想学习自动化(UI自动化到上手做项目)到上手,到能独立开发一个项目的UI自动化脚本. 一直在学习,边做边学,边看边学.边总结(具体看我的博客,其中大部分都是自己的理解 ...

  6. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

  7. CSS 随记

    伪类选择符的顺序:L-V-H-A CSS提供了四种元素设置链接的颜色,包括:link.:visited.:hover.:active,它们的声明是有一定顺序的,我们简称这种顺序为L-V-H-A,即li ...

  8. 改进持续交付中的CI环节

    改进持续交付中的CI环节 在当前 DevOps 的趋势下,持续集成(CI)和持续部署(CD)具有支柱性地位,那么能够成功搭建 CI/CD 流水线就至关重要了. 今天我就讲一讲如何做好CI部分,让我们的 ...

  9. NtCallbackReturn是否导致了用户态栈的不平衡

    0:000> u ntdll!KiFastSystemCall ntdll!KiFastSystemCall: 7c92eb8b 8bd4 mov edx,esp 7c92eb8d 0f34 s ...

  10. 哈希表(hash)详解

     哈希表结构讲解: 哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构.也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度. ...