2 条题解
-
0
#include<bits/stdc++.h> using namespace std; const int N = 5e6 + 10, inf = 1e9; vector<int>g[N], del, ng; int fr[N], bel[N], id[N], w[N], dis[N], vis[N], sz[N], is[N]; struct node { int x, t, d; friend bool operator < (const node &a, const node &b){return a.d>b.d;} }; priority_queue<node>pq; void upd(int x, int t) { int p = w[x-1] + 1 + t % sz[bel[x]]; if(t<dis[p]) dis[p] = t, pq.push((node){x,t%sz[bel[x]],t}); } int main() { int n, m, i, j, a, b; scanf("%d%d", &n, &m); while(m--) scanf("%d%d", &a, &b), g[a].emplace_back(b), g[b].emplace_back(a); int k;scanf("%d", &k); for(i=1;i<=k;i++) { scanf("%d", &sz[i]); for(j=0;j<sz[i];j++) scanf("%d", &a), bel[a] = i, id[a] = j; } sz[0] = 1; for(i=1;i<=n;i++) w[i] = w[i-1] + sz[bel[i]]; for(i=2;i<=w[n];i++) dis[i] = inf; pq.push((node){1,0,0}); while(!pq.empty()) { int d = pq.top().x, p = pq.top().t;pq.pop(); if(vis[w[d-1]+p+1]) continue; vis[w[d-1]+p+1] = 1, del.clear(); int t = dis[w[d-1]+p+1]; if(bel[d]&&id[d]!=(t+1)%sz[bel[d]]) upd(d,t+1); //printf("%d %d %d\n", d, p, t); for(auto y:g[d]) { int z = bel[y]; if(!bel[d]) { if(z) { if((t+1)%sz[z]!=id[y]) upd(y,t+1); int tt = t + (id[y] - t % sz[z] + sz[z]) % sz[z]; upd(y,tt+1); } else upd(y,t+1); } else if(!z) upd(y,t+1), del.emplace_back(y); else { int r = (id[y] - id[d] + sz[z]) % sz[z]; if(r==1&&bel[d]==z) upd(y,t+1); else if(r==sz[z]-1&&bel[d]==z) { if(id[y]!=(t+1)%sz[z]&&id[y]!=t%sz[z]) upd(y,t+1); } else { if((t+1)%sz[z]!=id[y]) upd(y,t+1); int T = t + (id[y] - t % sz[z] + sz[z]) % sz[z];//注意这里可能在 x 的时候 if(T%sz[bel[d]]!=id[d]) upd(y,T+1), del.emplace_back(y); else { int t1 = T + (t - T % sz[bel[d]] + sz[bel[d]]) % sz[bel[d]]; int T1 = t1 + (id[y] - t1 % sz[z] + sz[z]) % sz[z]; if((t1+1)%sz[z]!=id[y]) upd(y,t1+1); if(T1%sz[bel[d]]!=id[d]) upd(y,T1+1); } } } } for(auto i:del) is[i] = 1; ng.clear(); for(auto i:g[d]) if(!is[i]) ng.emplace_back(i); swap(g[d],ng), ng.clear(); for(auto i:del) is[i] = 0; } if(dis[w[n]]==inf) printf("impossible\n"); else printf("%d\n", dis[w[n]]); return 0; } -
0
太难了太难了太难了。
- 复杂度分析,拆点,定义域值域互换,最短路形优化 dp 转移。
显然你可以估计答案的上界,然后有一个 表示 时刻在 是否可行。定义 表示 所在的限制路径长度。考虑 是否在某个限制环上,对于 不在限制路径上的情况, 的 不存在或者是一段后缀;在限制路径上的情况,对于 同余的 要么不存在 要么是一段后缀。那么可以定义域值域互换,设计 表示到达 时刻模 为 的最小时刻,如果不在限制路径上 。考虑一下 的转移,要求 或者 ,且 时刻没有怪兽在 ,且 时刻没有 的怪兽。那么就是, 使得 。如果 同环就要求,不允许 时刻是 且 时刻是 。令 ,直接做的复杂度会高达 ,这是计算转移的数目得出的。具体使用 dijkstra 来转移。我们的目标是减少无效转移数量。只保留有效的转移。
对转移具体分类讨论:
- 均不在限制环上:可以直接 dijkstra 转移,这样的 只用转移一次;事实上同理的, 不在限制环但 在限制环是相同的。该类转移总复杂度 。
- 不在限制环但 在限制环的情况:我们希望减少无效的转移。注意到对于 等待 时刻到 在某些情况下可以转化成 等待 时刻再到 等待 时刻。那么我们希望将大部分 的转移转化成后者,那么考虑 无法被转化的情况,就是中间无法避开守卫的情况。找到最小的 表示在严格大于 之后第一个被守卫经过的时刻,那么我们可以只转移: 以及 。注意 时是无法执行前者的转移的。该类转移总复杂度 。
说明一下:如果一条边是理论最优的,可以从边的备选集合中删除。
- 均在限制环的情况,需要考虑 是否同环。如果 同环,注意特殊性质保证了只存在该类转移。分讨一下正序和逆序,用类似上一类的思想,对于 可以向 个 进行转移。也就是说,在具体转移时只需要考虑 的情况。即最小时刻即为需要考虑的。该类转移总复杂度 。
- 重点在于 均在限制环并且 不同环的情况。我们想要干的事情是:对于两个相邻的不同环 ,将转移的次数控制在 。类似的,我们考虑 表示在环 上 的后继。与之不同的是,我们并不能毫无顾忌的在 上一直等待,否则可能会碰见怪兽。考虑在 时刻时环 的位置上并没有怪兽。那么你可以提前到 然后立刻到 进行一个折返,这样就可以转移到 更后面的状态。此时这个转移时理论最优的,因此可以删去;
- 如果 时刻 有怪兽,那么尽管对于 之前的 可以执行转移,但是对于 及以后的状态而言,考虑找到 时刻表示 之后首个模 为 的时刻,对于 类似的找像 一样的后继 ,判定 时刻时 位置是否有怪兽,执行正常的转移。我们想要说明的是,转更多圈是没有意义的:这个情形是循环坠入的,转更多圈得到的情况与此相同。注意,此时边 的转移不一定理论最优,因此不能从边的备选集合中删去。
考虑分析一下不同环间转移的复杂度:对于第一次找 的 组可以消除到剩余最多 条边,但是对于第二次虽然会造成 个状态的访问,但是只会访问这么多。因此对于一个点向下一个考虑是 个的。乘以 就是 的环间转移。求和可以得到 的复杂度。
- 1
信息
- ID
- 10567
- 时间
- 4000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者