poj1330
题意:求祖先,解释了啥是LCA。
想法:模板。
#include
#include
#include
#include
using namespace std;
#define M 100009
int f[M],in[M],ancestor[M];
vector<int> tree[M],query[M];
int t,n,a,b,root,qa,qb;
bool vis[M];
int find(int x)
{
return x == f[x] ? f[x] : f[x] = find(f[x]);
}
int uni(int x,int y)
{
x = find(x);
y = find(y);
if(x != y) f[x] = y;
}
void init()
{
for(int i = 0; i <= n; i++)
{
vis[i] = false;
tree[i].clear();
query[i].clear();
in[i] = 0;
f[i] = i;
}
}
void tarjan(int x)
{
for(int i = 0; i < tree[x].size(); i++)
{
int v = tree[x][i];
tarjan(v); //先处理子树
uni(x,v); //将儿子节点的集合和父亲节点的集合合并
ancestor[find(x)] = x; //将该节点所在集合的代表的祖先设置为x
}
vis[x] = true;
for(int i = 0; i < query[x].size(); i++)
{
int v = query[x][i];
if(vis[v])
{
printf("%d\n",ancestor[find(v)]);
}
}
}
int main()
{
while(scanf("%d",&t) == 1)
{
while(t--)
{
scanf("%d",&n);
init();
for(int i = 0; i < n - 1; i++)
{
int a,b;
scanf("%d %d",&a,&b);
tree[a].push_back(b);
in[b]++;
}
scanf("%d %d",&qa,&qb);
query[qa].push_back(qb);
query[qb].push_back(qa);
for(int i = 1; i <= n; i++)
if(in[i] == 0) root = i;
tarjan(root);//从根节点开始tarjan
}
}
return 0;
}
hdu2586 (存模板XD)
题意:给你一些小屋之间的距离,问任意小屋之间的距离。
想法:用lca来求祖先,用祖先之间的距离进行转换。
#include
using namespace std;
const int maxn=55000;
vector<int>v[maxn],w[maxn],query[maxn],num[maxn];
///v表示连接的点,w表示连接之间的距离,query表示询问谁与谁之间,num表示询问的编号,利于最后直接输出是第几个询问的结果
int f[maxn],dis[maxn],ans[maxn];///dis表示根到这点的距离
bool vis[maxn];
int n;
void init()
{
for(int i=1;i<=n;++i)
{
v[i].clear();
w[i].clear();
query[i].clear();
num[i].clear();
f[i]=i;
dis[i]=0;
vis[i]=false;///n个地方起初全都未遍历
}
}
int Find(int x)
{
if(f[x]==x)
return x;
else
return Find(f[x]);
}
void join(int x,int y)
{
int t1 = Find(x);
int t2= Find(y);
if(t1!=t2)
f[t2]=t1;
//if(x == y) return;
//f[y] = x;
}
void tarjan(int cur,int val)///当前的点 根到这个点的距离
{
vis[cur]=true;
dis[cur]=val;
int size=v[cur].size();///当前以此点为根的集合的大小
for(int i=0;iint tmp=v[cur][i];///当前根与它集合内的这些点
if(vis[tmp])///这个点已经访问过了 就去看下一个点
continue;
else///如果没被访问过
tarjan(tmp,val+w[cur][i]);///没被访问的这个点就要当做根
join(cur,tmp);///然后把这两个集合合并
}
int len=query[cur].size();
for(int i=0;iint tmp=query[cur][i];///表示cur与其集合内的第几个点进行询问
if(!vis[tmp])
continue;
ans[num[cur][i]]=dis[cur]+dis[tmp]-2*dis[Find(tmp)];
}
}
int main()
{
int T,Q,x,y,z;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&Q);
init();
for(int i=1;iscanf("%d%d%d",&x,&y,&z);
v[x].push_back(y);
w[x].push_back(z);
v[y].push_back(x);
w[y].push_back(z);
}
for(int i=0;iscanf("%d%d",&x,&y);
query[x].push_back(y);
query[y].push_back(x);
num[x].push_back(i);
num[y].push_back(i);
}
tarjan(1,0);
for(int i=0;iprintf("%d\n",ans[i]);
}
return 0;
}
hdu 2874
题意:题意和2586差不多。
想法:麻烦点在于数据很大,用vector过不了,可以改用struct。
#include
#include
#include
using namespace std;
const int maxn = 10050;
int N, M, C;
struct node
{
int id, next, len;
} E[maxn << 1];
int head[maxn << 1], num;
void initlist()
{
memset(head, -1, sizeof(head));
num = 0;
}
void adde(int u, int v, int len)
{
E[num].id = u;
E[num].len = len;
E[num].next = head[v];
head[v] = num++;
}
int fa[maxn<<1];
void initfa()
{
for(int i = 0; i <= N; i++)
fa[i] = i;
}
int Find(int id)
{
if(id == fa[id])
return id;
else
return fa[id] = Find(fa[id]);
}
void addu(int u, int v)
{
int x = Find(u);
int y = Find(v);
if(x != y) fa[x] = y;
}
int p[16][maxn], dep[maxn], dis[maxn], vis[maxn];
void DFS(int u, int FA)
{
vis[u] = 1;
for(int l = head[u]; l != -1; l = E[l].next)
{
int id = E[l].id;
if(id == FA||vis[id]) continue;
dep[id] = dep[u] + 1;
dis[id] = dis[u] + E[l].len;
p[0][id] = u;
DFS(id, u);
}
}
void initlca()
{
memset(p, -1, sizeof(p));
memset(dep,0,sizeof(dep));
memset(dis, 0, sizeof(dis));
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= N; i++)
{
if(!vis[i])
DFS(i, -1);
}
for(int k = 0; k + 1 <= 15; k++)
for(int u = 1; u <= N; u++)
{
if(p[k][u] < 0) p[k + 1][u] = -1;
else
p[k + 1][u] = p[k][p[k][u]];
}
}
int LCA(int u, int v)
{
if(dep[u] > dep[v]) swap(u, v);
for(int k = 0; k <= 15; k++)
{
if((dep[v] - dep[u]) >> k & 1)
v = p[k][v];
}
if(u == v) return u;
for(int k = 15; k >= 0; k--)
{
if(p[k][u] != p[k][v])
{
u = p[k][u];
v = p[k][v];
}
}
return p[0][u];
}
int main ()
{
while(~scanf("%d%d%d", &N, &M, &C))
{
initlist();
initfa();
int u, v, ds;
for(int i = 1; i <= M; i++)
{
scanf("%d%d%d", &u, &v, &ds);
adde(u, v, ds);
adde(v, u, ds);
addu(u, v);
}
initlca();
for(int i=1;i<=C;i++)
{
scanf("%d%d",&u,&v);
int x=Find(u);
int y=Find(v);
if(x==y)
{
printf("%d\n",dis[u]+dis[v]-2*dis[LCA(u,v)]);
}
else
printf("Not connected\n");
}
}
return 0;
}
poj2763
题意:村子里一堆小木屋,麻麻在一个屋里,小盆友在其他屋里喊麻麻带他回家。麻麻想让你帮她算出来她还有多久能接到下一个孩子。
当有0出现时,后面是孩子所在的小屋;当有1出现时 去小屋的时间要因外界原因发生改变,并且还要去到这个小屋。
想法:刚开始想的就是 以麻麻为祖先 她去接下一个孩子的时间=她从起点直接接下一个孩子的时间-她从起点接当前孩子的时间
但是这样的时间必须就是一定了 没办法改变时间 又想到用线段树
鹅鹅鹅鹅鹅鹅套了线段树的板子虽然最后能够编译运行 但是wa了……
去学习别人的方法,发现有两种方法:1.lca+树状数组 2.lca+树链剖分
树链剖分太陌生了,马克一下,先看了树状数组。
每一条边对应着一段时间,运用时间戳(新玩意 查了一下是节点的搜索次序 dfs序)emmmmm先存一下。
ps:先写下来 万一哪天突然让我讲 不会讲就gg了