1 条题解
-
0
显然有一个想法是,确定了第一个人移动的轮数 后,每个人的答案是独立的,把以 为起点的人在移动 轮后的最小代价是 ,我们对 进行刻画。
首先对最优策略有一个观察,我们必然只有两种 case:
- 初始走到一个停止格 ,之后每次移动离开 再回到 。
- 先花若干步走到一个停止格 ,且其邻域内有一个停止格 ,之后每次移动在 间反复横跳。
第一种 case 较为容易,假设距离点 最近的停止点的距离为 ,那么有 。
第二种 case,假设我们花了 步到达一个合法的终止位置,总的距离为 ,那么我们浪费了 的时间,应该有 ,给每个终止节点 的权值,每条边 的权值,跑一遍最短路即可。
最后我们的 形如 。
记 ,那我们要解决的问题形如:
对于每个 ,找到一条 的路径,假设经过了 个终止点,路径长度为 ,我们要最小化 。
这个完全没法用数据结构维护,我们尝试使用一些根号做法。
假设 比较大,那么 增加 后, 至少增加 ,而 增大对 的贡献至多只有 ,也就是假设 的最小值是 ,那么我们只需要考虑 范围内的 即可,分层图最短路即可 。
比较小的时候,发现 是一个 段的凸的分段函数,并且每一段都是一条线段。
把线段变成直线,我们认为可以任选其中一条直线,然后一个惊人的发现是,这样不会把答案算小!因为我们是在一个凸包上面嘛,如果选错了直线答案必然更大。
那把这 条直线拉出来,把贡献拆到单点上面,跑最短路即可。时间复杂度 。
平衡一下,时间复杂度 。
corner 比较多,写起来也比较烦。 ::::info[code]
const int N=5e4+5,K=505; const ll inf=1e16; int n,m,k; vector<int> to[N]; void add(int u,int v){to[u].pb(v),to[v].pb(u);} ll F[N]; int s[N],ty[N],tg[N]; int c1[N],c2[N]; int dis[N]; void solve2(){ // calculate c2 queue<int> q; memset(dis,0x3f,sizeof dis); for(int i=1;i<=n;i++)if(ty[i])q.push(i),dis[i]=0; while(!q.empty()){ int u=q.front(); q.pop(); for(int v:to[u])if(dis[v]>dis[u]+1){ dis[v]=dis[u]+1; q.push(v); } } for(int i=1;i<=n;i++)c2[i]=dis[i]-(ty[i]?0:2); } bool vis[N]; void solve1(){ // calculate c1 priority_queue<pii,vector<pii>,greater<pii> > pq; memset(dis,0x3f,sizeof dis); for(int i=1;i<=n;i++)if(tg[i])pq.push(mkp(0,i)),dis[i]=0; while(!pq.empty()){ int u=pq.top().se; pq.pop(); if(vis[u])continue; vis[u]=1; for(int v:to[u]){ int w=ty[u]?0:1; if(dis[v]>dis[u]+w){ dis[v]=dis[u]+w; pq.push(mkp(dis[v],v)); } } } for(int i=1;i<=n;i++)c1[i]=dis[i]; } ll dif[N],dif2[N]; vector<int> fg; void solve6(){ for(int j=2;j<=k;j++){ int i=s[j]; int _t=c1[i]-c2[i]; chkmin(_t,n+1),chkmax(_t,1); fg.pb(_t); dif[1]+=c2[i],dif[_t]+=c1[i]-c2[i]-_t+1; dif2[1]+=2,dif2[_t]--; } for(int i=1;i<=n;i++)dif2[i]+=dif2[i-1],dif[i]+=dif2[i]; for(int i=1;i<=n;i++)dif[i]+=dif[i-1],F[i]=dif[i]; fg.pb(1),fg.pb(n); fg.pb(n+1); sort(fg.begin(),fg.end()),fg.erase(unique(fg.begin(),fg.end()),fg.end()); } int d[N][K],_d[N]; void solve2p5(){ memset(_d,0x3f,sizeof _d); deque<int> q; q.push_front(s[1]); _d[s[1]]=0; while(!q.empty()){ int u=q.front(); q.pop_front(); for(int v:to[u]){ int dv=_d[u]+ty[v]; if(dv<_d[v]){ _d[v]=dv; if(!ty[v])q.push_front(v); else q.push_back(v); } } } } void solve3(){ memset(d,0x3f,sizeof d); queue<pii> q; q.push(mkp(s[1],0)); d[s[1]][0]=0; while(!q.empty()){ int u=q.front().fi,c=q.front().se; q.pop(); for(int v:to[u]){ int nd=d[u][c]+1,nc=c+ty[v]+_d[u]-_d[v]; if(nc>=K)continue; if(nd<d[v][nc]) d[v][nc]=nd,q.push(mkp(v,nc)); } } } ll ans[N],dd[N*3]; bool vv[N*3]; void solve5(ll b,ll k){ priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > pq; pq.push(mkp(0,s[1]*3)); memset(dd,0x3f,sizeof dd),memset(vv,0,sizeof vv); dd[s[1]*3]=0; while(!pq.empty()){ int u=pq.top().se; pq.pop(); if(vv[u])continue; vv[u]=1; for(int v:to[u/3]){ ll nd=dd[u]+1+ty[v]*k; int nv=v*3; int c=u%3; c+=ty[v]; chkmin(c,2); nv+=c; if(nd<dd[nv]) dd[nv]=nd,pq.push(mkp(dd[nv],nv)); } } for(int i=1;i<=n;i++)if(i!=s[1]){ chkmin(ans[i],b+dd[i*3+2]-ty[i]*k); if(!ty[i])chkmin(ans[i],b+dd[i*3+1]-ty[i]*k); } } void solve4(){ if(k<=150){ int ls=0; for(int i:fg){ if(ls && i<=n){ ll k=F[i]-F[i-1],b=F[i]-i*k; solve5(b,k); } ls=i; } } } void solve7(){ for(int i=1;i<=n;i++){ for(int j=0;j<K && j+_d[i]-ty[i]<=n;j++) chkmin(ans[i],F[j+_d[i]-ty[i]]+d[i][j]); } } int main(){ n=read(),m=read(),k=read(); while(m--)add(read(),read()); char c=gc(); while(!isdigit(c))c=gc(); for(int i=1;i<=n;i++)ty[i]=c-'0',c=gc(); for(int i=1;i<=k;i++)s[i]=read(); for(int i=1;i<=n;i++)for(int v:to[i]) if(ty[i]&&ty[v])tg[i]=1; memset(ans,0x3f,sizeof ans); solve2(),solve1(),solve6(); solve2p5(),solve3(),solve4(); solve7(); for(int i=1;i<=n;i++)printf("%lld\n",ans[i]); return 0; } // Think twice,code once::::
- 1
信息
- ID
- 7549
- 时间
- 4000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者