您的位置:首页 > 其它

codeforces 864 F

2017-09-27 20:54 302 查看
有向图,倍增。

思路:参考大佬的题解

倍增思想,比如求s到t的道路,那么先通过反向图dfs出所有可以到达t的节点。(相当于一棵树)如果s不在其中,输出-1.然后先用正向图的边把st数组初始化一下(用字典序最小的一个)。然后把st[t][0]置为n+1.其他剩余点都保持为0.然后在倍增完成后,如果st[ s ][ TOP ] ==n+1说明x最终可以到达t并且继续走到n+1.(2^TOP>3000).否则st[ s ][ TOP ] ==0说明s根本无法走到t。还有一种情况,st[ s ][ TOP ]!=0且!=n+1。那么就说明s进入了字典序循环中,无法走出。巧妙点就是这个把st[ t ][0]初始置为n+1。

学习了新的方法,对倍增又有了一个深刻的认识哇。

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 3000+5;
int n,m,q;
vector<int>head[MAXN],G[MAXN];//正反向图
int st[MAXN][13];//倍增数组
//询问
struct node
{
int s,k,id;
node(int s,int k,int id)
{
this->s = s;
this->k = k;
this->id = id;
}
};
vector<node>ask[MAXN];
//dfs找到可以到达t的点
bool vis[MAXN];
void dfs(int u)
{
vis[u] = 1;
for(int v : G[u])if(!vis[v])dfs(v);
}
//存答案
int ans[400005];

int main()
{
scanf("%d%d%d",&n,&m,&q);
int u,v;
while(m--)
{
scanf("%d%d",&u,&v);
head[u].push_back(v);
G[v].push_back(u);
}
for(int i = 1; i <= n; ++i)sort(head[i].begin(),head[i].end());
int s,t,k;
for(int i = 0; i < q; ++i)
{
scanf("%d%d%d",&s,&t,&k);
ask[t].push_back(node(s,k,i));
}
for(int t = 1; t <= n; ++t)
{
if(ask[t].size() == 0)continue;
fill(vis+1,vis+1+n,0);
dfs(t);
memset(st,0,sizeof st);
st[n+1][0] = n+1;
for(int u = 1; u <= n; ++u)
{
if(u == t)st[u][0] = n+1;
else if(vis[u])
{
for(int v : head[u])
{
if(!vis[v])continue;
st[u][0] = v;
break;
}
}
}
for(int i = 1; i <= 12; ++i)
for(int j = 1; j <= n+1; ++j)st[j][i] = st[st[j][i-1]][i-1];
//查询ask中的询问
for(node x : ask[t])
{
int s = x.s;
int k = x.k-1;
int id = x.id;
if(st[s][12] == n+1)
{
for(int i = 0; i <= 12; ++i)
{
if(k & (1<<i))s = st[s][i];
}
if(s != n+1)ans[id] = s;
else ans[id] = -1;
}
else ans[id] = -1;
}
}
for(int i = 0; i < q; ++i)
{
printf("%d\n",ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: