1. 程式人生 > 實用技巧 >CF593D Happy Tree Party

CF593D Happy Tree Party

前言

由於之前沒有寫過這類部落格,特地補一篇

題目

洛谷

CF

講解

如果你做過這道題: 上帝造題的七分鐘2 / 花神遊歷各國(洛谷)

那麼可能對你做這道題有幫助

我們發現,對於詢問,如果除數不等於\(1\),那麼在log級別的次數內,\(y_i\)即可被除為\(0\)

有了這個發現,我們就很容易求得答案

對於一次詢問,暴力找出這條路徑上所有不等於1的邊,一旦統計數超過\(60(log_2(1e18)\approx 60)\)

這時候就會有人問了:“如果暴力爬樹的時候全都是1的邊不就TLE了嗎?”

誒,我們用一個並查集就可以保證時間複雜度了,如果邊為1,我們直接把它在並查集中連到祖先去

這樣在下次就不會經過它了,從而保證時間複雜度

程式碼

int head[MAXN],tot;
struct edge
{
	int v,ID,nxt;
	LL w;
}e[MAXN << 1];
void Add_Edge(int x,int y,LL z,int ID)
{
	e[++tot].v = y;
	e[tot].w = z;
	e[tot].ID = ID;
	e[tot].nxt = head[x];
	head[x] = tot;
}
void Add_Double_Edge(int x,int y,LL z,int ID)
{
	Add_Edge(x,y,z,ID);
	Add_Edge(y,x,z,ID);
}

int f[MAXN][18],d[MAXN],eID[MAXN],fa[MAXN],bcj[MAXN];
LL w[MAXN];
int findSet(int x)
{
	if(bcj[x] != x) bcj[x] = findSet(bcj[x]);
	return bcj[x];
}
void dfs(int x,int FA)
{
	d[x] = d[FA] + 1;
	f[x][0] = FA;
	for(int i = 1;i <= 17;++ i) f[x][i] = f[f[x][i-1]][i-1];
	for(int i = head[x]; i ;i = e[i].nxt)
		if(!d[e[i].v])
			eID[e[i].ID] = e[i].v,w[e[i].v] = e[i].w,dfs(e[i].v,x);
}

int lca(int x,int y)
{
	if(d[x] < d[y]) swap(x,y);
	for(int i = 17;i >= 0;-- i) if(d[f[x][i]] >= d[y]) x = f[x][i];
	if(x == y) return x;
	for(int i = 17;i >= 0;-- i)
		if(f[x][i] != f[y][i])
			x = f[x][i],y = f[y][i];
	return f[x][0];
}
LL a[65],b[65];
void Get(int x,int LCA,LL *z,int &cnt)
{
	while(d[findSet(x)] > d[LCA] && cnt < 60)
	{
		x = findSet(x);
		if(w[x] == 1) bcj[x] = findSet(f[x][0]);
		else z[++cnt] = w[x];
		x = f[x][0];
	}
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); m = Read();
	for(int i = 1;i < n;++ i) 
	{
		int u = Read(),v = Read();
		Add_Double_Edge(u,v,Read(),i);
	}
	for(int i = 1;i <= n;++ i) bcj[i] = i;
	dfs(1,0);
	while(m--)
	{
		int opt = Read();
		if(opt == 1)
		{
			int u = Read(),v = Read(),cnta = 0,cntb = 0;
			LL val = Read();
			int LCA = lca(u,v);
			Get(u,LCA,a,cnta);
			Get(v,LCA,b,cntb);
			if(cnta + cntb >= 60) Put(0,'\n');
			else 
			{
				for(int i = 1;i <= cnta && val;++ i) val /= a[i];
				for(int i = cntb;i >= 1 && val;-- i) val /= b[i];
				Put(val,'\n');
			}
		}
		else
		{
			int p = Read();
			w[eID[p]] = Read();
		}
	}
	return 0;
}