DFS序例题+感受(已更新到6题)

最近在学dfs序的实际应用,专开个blog记录下感受
目前来看都是配合线段树,从树上问题变成区间问题,顺便吐槽下有些人的题集竟然不挂链接

求和

https://ac.nowcoder.com/acm/problem/204871
题面:
已知有 \(n\) 个节点,有 \(n - 1\) 条边,形成一个树的结构。

给定一个根节点 \(k\),每个节点都有一个权值,节点 \(i\) 的权值为 \(v_i\)

\(m\) 个操作,操作有两种类型:

1 \(ax\):表示将节点 \(a\) 的权值加上 \(x\)

2 \(a\):表示求 \(a\) 节点的子树上所有节点的和(包括 \(a\) 节点本身
输入:
第一行给出三个正整数 \(n, m, k\),表示树的节点数、操作次数、和这棵树的根节点。

第二行给出 \(n\) 个正整数,第 \(i\) 个正整数表示第 \(i\) 个节点的权值 \(val_i\)

下面 \(n - 1\) 行每行两个正整数 \(u, v\),表示边的两个端点。

接下来 \(m\) 行,每行给出一个操作。
输出:
对于每个类型为 2 的操作,输出一行一个正整数,表示以a为根的子树的所有节点的权值和
数据范围:
\( \begin{align*} 1 &\leq n, m \leq 1e6, 1 \leq k \leq n \\ 1 &\leq u, v \leq n \\ 1 &\leq a \leq n \\ -1e6 &\leq val_i, x \leq 1e6 \end{align*} \)
样例:
5 6 1
1 2 3 4 5
1 3
1 2
2 4
2 5
1 2 10
1 3 10
1 4 5
1 5 1
2 3
2 2
————————
13
27
感受:算是个很基本的入门题了,就是dfs序+线段树(单点修改+区间查询),直接一遍过了

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                   long long 
#define lowbit(x) (x & -x)
//#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
struct s
{
	ll v,l,r;
	ll lazy;
}p[1000002<<2];
void build(ll i,ll l,ll r)
{
	p[i].v=0;
	p[i].l=l,p[i].r=r;
	if(l==r)
	return ;
	build(i<<1,l,(l+r)>>1);
	build(i<<1|1,(l+r>>1)+1,r);
}
void update(ll i,ll l,ll r,ll v)
{
	if(l==p[i].l&&r==p[i].r)
	{
		p[i].v+=v;
		return ;
	}
	ll mid=(p[i].l+p[i].r)>>1;
	if(l<=mid)
	update(i<<1,l,min(mid,r),v);
	if(r>=mid+1)
	update(i<<1|1,max(mid+1,l),r,v);
	p[i].v=p[i<<1].v+p[i<<1|1].v;
}
ll query(ll i,ll l,ll r)
{
	ll ans=0;
	if(l==p[i].l&&r==p[i].r)
	{
		ans+=p[i].v;
		return ans;
	}
	ll mid=(p[i].l+p[i].r)>>1;
	if(l<=mid)
	ans+=query(i<<1,l,min(mid,r));
	if(r>=mid+1)
	ans+=query(i<<1|1,max(mid+1,l),r);
	return ans;
}
ll a[1000002];
ll ne[2000002];
ll to[2000002];
ll h[2000002];
ll b[2000002];
ll cnt=0;
ll gs=0;
void add(ll l,ll r)
{
	to[cnt]=r;
	ne[cnt]=h[l];
	h[l]=cnt++;	
}
void dfs(ll k,ll fa)
{
	gs++;
	b[gs]=k;
	for(ll i=h[k];i>=0;i=ne[i])
	{
		if(to[i]==fa)continue;
		dfs(to[i],k);
	}
	gs++;
	b[gs]=k;
}
ll vis[2000002];
pair<ll,ll>ans[1000002];
int main()
{
	fio();
ll n,m,k;
cin>>n>>m>>k;
for(ll i=1;i<=n;i++)cin>>a[i];
memset(h,-1,sizeof h);
build(1,1,n);
for(ll i=1;i<=n-1;i++)
{
ll l,r;
cin>>l>>r;
add(l,r);
add(r,l);
}
dfs(k,0);
cnt=0;
ll last=0;
for(ll i=1;i<=gs;i++)
{
if(vis[b[i]]==0)
{
   cnt++;
update(1,cnt,cnt,a[b[i]]);
  vis[b[i]]=cnt;
  last=cnt;
}
else 
{
ans[b[i]]={vis[b[i]],last};
}
}
//cout<<query(1,1,5)<<endl;
while(m--)
{
	ll op;
	cin>>op;
	switch(op)
	{
		case 1:
		{
			ll x,y;
			cin>>x>>y;
			update(1,vis[x],vis[x],y);
			break;
		}
		case 2:
		{
			ll x;
			cin>>x;
			cout<<query(1,ans[x].first,ans[x].second)<<endl;
			break;
		}
	}
}
}

Apple Tree

http://poj.org/problem?id=3321
题面:
在卡卡家外面有一棵苹果树。每到秋天,树上就会结出很多苹果。卡卡非常喜欢苹果,所以他一直在精心培育这棵大苹果树。

这棵树有N个分叉,这些分叉通过树枝相连。卡卡用1到N的数字给分叉编号,根分叉总是编号为1。苹果会生长在分叉上,而同一个分叉上不会同时长出两个苹果。卡卡想知道一个子树上有多少个苹果,以研究苹果树的产量。

问题是,一个新的苹果可能随时会在空分叉上生长出来,卡卡也可能为了他的甜点从树上摘一个苹果。你能帮帮卡卡吗?

输入:
第一行包含一个整数N(N ≤ 100,000),表示树的分叉数。
接下来的N - 1行每行包含两个整数u和v,意味着分叉u和分叉v通过一个树枝相连。
下一行包含一个整数M(M ≤ 100,000)。
接下来的M行每行包含一个消息,消息是以下两种之一:
"C x" 表示分叉x上苹果的存在状态已经改变。即如果分叉上有苹果,那么卡卡就摘走了它;否则,一个新苹果在空分叉上生长。
或者
"Q x" 表示询问分叉x上方子树上的苹果数量,包括分叉x上存在的苹果(如果存在)。
注意,树最初是满苹果的。
输出:
样例:
3
1 2
1 3
3
Q 1
C 2
Q 1
——————
3
2
思路:写法与上道题一样,但是POJ好像不支持vector建树?这样写会TLE,得用链式前向星进行优化,这样就不会TLE了,这里给出树状数组写法

#include<iostream>
    #include<queue>
    #include<map>
    #include<set>
    #include<stdio.h>
    #include<cstdio>
    #include<vector>
//  #include<bits/stdc++.h>
    #include<algorithm>
    #include<deque>
    #include<cctype>
    #include<string.h>
    #include<math.h>
    #include<time.h>
    // #include<random>
    #include<stack>
    #include<string>
    #define ll                                int
    #define lowbit(x) (x & -x)
    #define endl "\n"//                           交互题记得删除
    using namespace std;
    // mt19937 rnd(time(0));
    const ll mod = 998244353;
    const ll maxn = 1e5+5;
    void fio()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
    }
    //vector<ll>g[100015];
    ll cnt=0;
    ll h[maxn];
    ll ne[maxn<<1];
    ll to[maxn<<1];
    void ad(ll x,ll y)
    {
        to[cnt]=y;
        ne[cnt]=h[x];
        h[x]=cnt;
        cnt++;
    }
    ll c1[maxn];
    ll c2[maxn];
    ll gs = 0;
    pair<ll, ll>ans[maxn];
    void dfs(ll k, ll fa)
    {
        gs++;
        ll j=gs;
        for(ll i=h[k];i>=0;i=ne[i])
        {
            if(to[i]==fa)continue;
            dfs(to[i],k);
        }
        ans[k]={j,gs};
    }
    ll n;
    void add(ll *t,ll x,ll v)
    {
        for(ll i=x;i<=n;i+=lowbit(i))
        t[i]+=v;
        return ;
    }
    ll ask(ll* t,ll x)
    {
        ll ans=0;
        for(ll i=x;i>0;i-=lowbit(i))
        {
            ans+=t[i];
        }
        return ans;
    }
    ll q(ll l,ll r)
    {
        return r*ask(c1,r)-ask(c2,r)-((l-1)*ask(c1,l-1)-ask(c2,l-1));
    }
    inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = x * 10 + ch - '0', ch = getchar();
    return x * f;
}
    int main()
    {
        //fio();
        gs=0;
        memset(h,-1,sizeof h);
        n=read();
        for(ll i=1;i<=n-1;i++)
        {
            ll l,r;
            l=read();
            r=read();
            ad(l,r);
            ad(r,l);
        }
        add(c1,1,1);
        add(c2,1,0);
        dfs(1,-1);
        ll m;
        m=read();
        while(m--)
        {
            ll x;
            char f[5];
            scanf("%s",&f);
            x=read();
            if(f[0]=='Q')
            {
            printf("%d\n",q(ans[x].first,ans[x].second));
            }
            else 
            {
                if(q(ans[x].first,ans[x].first)==1)
                {
                    add(c1,ans[x].first,-1);
                    add(c1,ans[x].first+1,1);
                    add(c2,ans[x].first,(ans[x].first-1)*(-1));
                    add(c2,ans[x].first+1,ans[x].first*(1));
                }
                else 
                {
                    add(c1,ans[x].first,1);
                    add(c1,ans[x].first+1,-1);
                    add(c2,ans[x].first,(ans[x].first-1)*(1));
                    add(c2,ans[x].first+1,ans[x].first*(-1));
                }
            }
        }
        return 0;
    }

华华和月月种树

https://ac.nowcoder.com/acm/problem/23051
题面:
华华看书了解到,一起玩养成类的游戏有助于两人培养感情。所以他决定和月月一起种一棵树。因为华华现在也是信息学高手了,所以他们种的树是信息学意义下的。

华华和月月一起维护了一棵动态有根树,每个点有一个权值。刚开存档的时候,树上只有0号节点,权值为0。接下来有两种操作:

操作1: 输入格式 \(1i\),表示月月氪金使节点 \(i\) 长出了一个新的儿子节点,权值为0,编号为当前最大编号+1(也可以理解为,当前是第几个操作 1,新节点的编号就是多少)。

操作2: 输入格式 \(2ia\),表示华华上线做任务使节点 \(i\) 的子树中所有节点(即它和它的所有子孙节点)权值加 \(a\)

但是月月有时会检查华华有没有认真维护这棵树,会作出询问:

询问3: 输入格式 \(3i\),华华需要给出 \(i\) 节点此时的权值。

华华当然有认真种树了,不过还是希望能写个程序以备不时之需。
输入:
第一行一个正整数 \(M\),接下来 \(M\) 行,每行先输入一个正整数 \(O\) 表示操作类型,再输入一个非负整数 \(i\) 表示操作或询问的节点编号,如果 \(O=2\),再输入一个正整数 \(a\)
输出:
对于每个询问 \(3\),输出一个非负整数表示询问的答案。
样例:
9
1 0
2 0 1
3 0
3 1
1 0
1 1
2 0 2
3 1
3 3
——————
1
1
3
2
数据范围:
\(1 \leq M \leq 4 \times 10^5\),保证操作1的数量不超过 \(10^5\),保证操作2中的参数 \(a\) 满足 \(1 \leq a \leq 999\)
感受:感觉不难,但是错了快15次吧,为什么呢?一是手敲线段树,区间修改漏了每次都是修改区间值,二是认为可以二分要修改的范围,这里不是子树嵌套子树,所以二分不行。三是每次遇到新节点时,得减少值,这里直接问了区间和,然后区间里每个数减去区间和了。被自己逗笑了。思路就是离线+dfs序+线段树(区间修改+单点查询)

#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                                   long long 
#define lowbit(x) (x & -x)
//#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 4e5 + 5;
void fio()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
}
struct s
{
	ll v, l, r;
	ll lazy;
}p[maxn << 2];
void build(ll i, ll l, ll r)
{
	p[i].v = 0;
	p[i].l = l, p[i].r = r;
	p[i].lazy = 0;
	if (l == r)
		return;
	build(i << 1, l, (l + r) >> 1);
	build(i << 1 | 1, (l + r >> 1) + 1, r);
}
void push_down(ll i)
{
	if (p[i].lazy)
	{
		p[i << 1].v += (p[i << 1].r - p[i << 1].l + 1) * p[i].lazy;
		p[i << 1 | 1].v += (p[i << 1 | 1].r - p[i << 1 | 1].l + 1) * p[i].lazy;
		p[i << 1 | 1].lazy += p[i].lazy;
		p[i << 1].lazy += p[i].lazy;
		p[i].lazy = 0;
	}
}
void update(ll i, ll l, ll r, ll v)
{
	if (l == p[i].l && r == p[i].r)
	{
		p[i].v += (r-l+1)*v;
		p[i].lazy += v;
		return;
	}
	push_down(i);
	ll mid = (p[i].l + p[i].r) >> 1;
	if (l <= mid)
		update(i << 1, l, min(mid, r), v);
	if (r >= mid + 1)
		update(i << 1 | 1, max(mid + 1, l), r, v);
	p[i].v = p[i << 1].v + p[i << 1 | 1].v;
}
ll query(ll i, ll l, ll r)
{
	ll ans = 0;
	if (l == p[i].l && r == p[i].r)
	{
		ans += p[i].v;
		return ans;
	}
	push_down(i);
	ll mid = (p[i].l + p[i].r) >> 1;
	if (l <= mid)
		ans += query(i << 1, l, min(mid, r));
	if (r >= mid + 1)
		ans += query(i << 1 | 1, max(mid + 1, l), r);
	return ans;
}
vector<ll>g[500000];
ll a[maxn];
ll b[maxn << 1];
ll cnt = 0;
ll gs = 0;
void dfs(ll k, ll fa)
{
	gs++;
	b[gs] = k;
	for (auto j : g[k])
	{
		dfs(j, k);
	}
	gs++;
	b[gs] = k;
}
ll vis[maxn];
ll vi[maxn];
ll d[maxn];
pair<ll, ll>ans[maxn];
struct u
{
	ll x;
	ll l, r;
}f[maxn];
int main()
{
	fio();
	ll n;
	cin >> n;
	build(1, 1, n);
	ll o = 1;
	ll cs = 0;
	for (ll i = 1; i <= n; i++)
	{
		cin >> f[i].x;
		if (f[i].x == 2)cin >> f[i].l >> f[i].r;
		else
		{
			cin >> f[i].l;
			if (f[i].x == 1)
			{
				f[i].r=o;
				g[f[i].l].push_back(o);	
				o++;
			}
		}
	}
	dfs(0, -1);
	cnt = 0;
	for (ll i = 1; i <= gs; i++)
	{
		if (vis[b[i]] == 0)
		{
			cnt++;
			vis[b[i]] = cnt;
			d[cnt] = vi[b[i]];
		}
		else
		{
			ans[b[i]] = { vis[b[i]],cnt };
		}
	}
	cnt = 0;
	ll fs=0;
	for (ll i = 1; i <= n; i++)
	{
		if (f[i].x == 1)
		{
			ll u=query(1,ans[f[i].r].first,ans[f[i].r].first);
			update(1, ans[f[i].r].first, ans[f[i].r].first, -u);
		}
		else if (f[i].x == 2)
		{
			update(1, ans[f[i].l].first, ans[f[i].l].second, f[i].r);
		}
		else if (f[i].x == 3)
		{
			cout << query(1, ans[f[i].l].first, ans[f[i].l].first) << endl;
		}
	}
}

A Growing Tree
https://codeforces.com/contest/1891/problem/F
题面:
你给定了一个根在顶点 \(1\) 的有根树,最初只包含一个顶点。每个顶点都有一个数值,初始设置为 \(0\)。还有 \(q\) 个查询,有两种类型:

  • 第一种类型:向顶点 \(v\) 添加一个编号为 \(sz + 1\) 的子顶点,其中 \(sz\) 是树的当前大小。新顶点的数值将为 \(0\)

  • 第二种类型:将 \(x\) 加到顶点 \(v\) 的子树中所有顶点的数值上。

在所有查询完成后,输出最终树中所有顶点的数值。
输入:
第一行包含一个整数 \(T\)\(1 \leq T \leq 10^4\))——测试用例的数量。测试用例的描述如下。

每个测试用例的第一行包含一个整数 \(q\)\(1 \leq \leq 5 \cdot 10^5\))——查询的数量。

接下来的 \(q\) 行可以归为两种情况:

第一种类型的查询:第 \(i\) 行包含两个整数 \(t_i\)\(t_i=1\)),\(v_i\)。你需要向顶点 \(v_i\) 添加一个编号为 \(sz+1\) 的子顶点,其中 \(sz\) 是树的当前大小。保证 \(1 \leq v_i \leq sz\)

第二种类型的查询:第 \(i\) 2 行包含三个整数 \(t_i\)\(t_i=2\)),\(v_i\)\(x_i\)\(-10^9 \leq x_i \leq 10^9\))。你需要将 \(x_i\) 添加到 \(v_i\) 的子树中所有顶点的数值上。保证 \(1 \leq v_i \leq sz\),其中 \(sz\) 是树的当前大小。

保证所有测试用例中 \(q\) 的总和不超过 \(5 \cdot 10^5\)
输出:
对于每个测试用例,在所有查询执行完毕后,输出最终树中每个顶点的数值。
样例:
3
9
2 1 3
1 1
2 2 1
1 1
2 3 2
1 3
2 1 4
1 3
2 3 2
5
2 1 1
1 1
2 1 -1
1 1
2 1 1
5
1 1
1 1
2 1 1
2 1 3
2 2 10
——————
7 5 8 6 2
1 0 1
4 14 4
感受:这个是上面的华华和月月种树弱化版,但是很奇怪我线段树开5e5+5的四倍空间竟然越界了,改成可5e5+10就过了,有点神奇。dfs序+线段树(区间修改+单点查询)

	#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<vector>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	#include<random>
	#include<stack>
	#include<string>
	#define ll                                  long long
	#define lowbit(x) (x & -x)
	//#define endl "\n"//                           交互题记得删除
	using namespace std;
	mt19937 rnd(time(0));
	const ll mod = 998244353;
	const ll maxn = 5e5 + 10;
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	struct s
	{
		ll v, l, r;
		ll lazy;
	}p[maxn << 2];
	void build(ll i, ll l, ll r)
	{
		p[i].v = 0;
		p[i].l = l, p[i].r = r;
		p[i].lazy = 0;
		if (l == r)
			return;
		build(i << 1, l, (l + r) >> 1);
		build(i << 1 | 1, (l + r >> 1) + 1, r);
	}
	void push_down(ll i)
	{
		if (p[i].lazy)
		{
			p[i << 1].v += (p[i << 1].r - p[i << 1].l + 1) * p[i].lazy;
			p[i << 1 | 1].v += (p[i << 1 | 1].r - p[i << 1 | 1].l + 1) * p[i].lazy;
			p[i << 1 | 1].lazy += p[i].lazy;
			p[i << 1].lazy += p[i].lazy;
			p[i].lazy = 0;
		}
	}
	void update(ll i, ll l, ll r, ll v)
	{
		if (l == p[i].l && r == p[i].r)
		{
			p[i].v += (r-l+1)*v;
			p[i].lazy += v;
			return;
		}
		push_down(i);
		ll mid = (p[i].l + p[i].r) >> 1;
		if (l <= mid)
			update(i << 1, l, min(mid, r), v);
		if (r >= mid + 1)
			update(i << 1 | 1, max(mid + 1, l), r, v);
		p[i].v = p[i << 1].v + p[i << 1 | 1].v;
	}
	ll query(ll i, ll l, ll r)
	{
		ll ans = 0;
		if (l == p[i].l && r == p[i].r)
		{
			ans += p[i].v;
			return ans;
		}
		push_down(i);
		ll mid = (p[i].l + p[i].r) >> 1;
		if (l <= mid)
			ans += query(i << 1, l, min(mid, r));
		if (r >= mid + 1)
			ans += query(i << 1 | 1, max(mid + 1, l), r);
		return ans;
	}
	vector<ll>g[500015];
	ll b[maxn << 1];
	ll cnt = 0;
	ll gs = 0;
	void dfs(ll k, ll fa)
	{
		gs++;
		b[gs] = k;
		for (auto j : g[k])
		{
			dfs(j, k);
		}
		gs++;
		b[gs] = k;
	}
	ll vis[maxn];
	pair<ll, ll>ans[maxn];
	struct u
	{
		ll x;
		ll l, r;
	}f[maxn];
	int main()
	{
		fio();
		ll t;
		cin>>t;
		while(t--)
		{
		gs=0;
		ll n;
		cin >> n;
		build(1, 1, n+5);
		ll o = 2;
		for (ll i = 1; i <= n; i++)
		{
			g[i].clear();
			cin >> f[i].x;
			if (f[i].x == 2)cin >> f[i].l >> f[i].r;
			else
			{
				cin >> f[i].l;
				f[i].r=o;
				g[f[i].l].push_back(o);	
				o++;
			}
		}
		dfs(1, -1);
		cnt = 0;
		for(ll i=1;i<=gs;i++)vis[b[i]]=0;
		for (ll i = 1; i <= gs; i++)
		{
			if (vis[b[i]] == 0)
			{
				cnt++;
				vis[b[i]] = cnt;
			}
			else
			{
				ans[b[i]] = { vis[b[i]],cnt };
			}
		}
		cnt = 0;
		ll fs=0;
		for (ll i = 1; i <= n; i++)
		{
			if (f[i].x == 1)
			{
				ll u=query(1,ans[f[i].r].first,ans[f[i].r].first);
				update(1, ans[f[i].r].first, ans[f[i].r].first, -u);
			}
			else if (f[i].x == 2)
			{
				update(1, ans[f[i].l].first, ans[f[i].l].second, f[i].r);
			}
		}
		o--;
		for(ll i=1;i<=o;i++)
		{
			cout<<query(1,ans[i].first,ans[i].first)<<" ";
		}
		cout<<endl; 
		}
	}

Snacks

https://acm.hdu.edu.cn/showproblem.php?pid=5692
题面:
百度科技园内有n个零食机,零食机之间通过n−1条路相互连通。每个零食机都有一个值v,表示为小度熊提供零食的价值。

由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化。小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次。另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机。

为小度熊规划一个路线,使得路线上的价值总和最大。
输入:
输入数据第一行是一个整数T(T≤10),表示有T组测试数据。

对于每组数据,包含两个整数n,m(1≤n,m≤100000),表示有n个零食机,m次操作。

接下来n−1行,每行两个整数x和y(0≤x,y<n),表示编号为x的零食机与编号为y的零食机相连。

接下来一行由n个数组成,表示从编号为0到编号为n−1的零食机的初始价值v(|v|<100000)。

接下来m行,有两种操作:0 x y,表示编号为x的零食机的价值变为y;1 x,表示询问从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。

本题可能栈溢出,辛苦同学们提交语言选择c++,并在代码的第一行加上:

#pragma comment(linker, "/STACK:1024000000,1024000000")
输出:
对于每组数据,首先输出一行”Case #?:”,在问号处应填入当前数据的组数,组数从1开始计算。

对于每次询问,输出从编号为0的零食机出发,必须经过编号为x零食机的路线中,价值总和的最大值。
样例:
1
6 5
0 1
1 2
0 3
3 4
5 3
7 -5 100 20 -5 -7
1 1
1 3
0 2 -1
1 1
1 5
————————
Case #1:
102
27
2
20
思路:我也是第一次做区间修改+最大值维护的,之前最多也就是单点修改+最大值维护。
但是如果对于线段树有理解的画,应该不会太难。每次加减值一定会把这个被修改的区间的最大值也会相应的加减值
离线+dfs序+线段树(区间修改+区间最大).我这里代码交G++才不会超时,C++会超时

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<deque>
#include<cctype>
#include<string.h>
#include<math.h>
#include<time.h>
#include<random>
#include<stack>
#include<string>
#define ll                             long long
#define lowbit(x) (x & -x)
#define endl "\n"//                           交互题记得删除
using namespace std;
mt19937 rnd(time(0));
const ll mod = 998244353;
const ll maxn = 100000 + 15;
struct s
{
    ll l, r;
    ll v1;
    ll ad, v, cf;//ad为加法的懒惰标记,cf为乘法的懒惰标记
}p[maxn << 2];
void build(ll i, ll l, ll r)
{
    p[i].l = l, p[i].r = r, p[i].v = 0;
    p[i].ad = 0;
    if (l == r)
    {
        return;
    }
    build(i << 1, l, (l + r) >> 1);
    build(i << 1 | 1, ((l + r) >> 1) + 1, r);
}
void push_down(ll i)
{
    if (p[i].ad)
    {
        p[i << 1].v += p[i].ad;
        p[i << 1 | 1].v += p[i].ad;
        p[i << 1].ad += p[i].ad;
        p[i << 1 | 1].ad += p[i].ad;
        p[i].v = max(p[i << 1].v, p[i << 1 | 1].v);
        p[i].ad = 0;
    }
}
void udq(ll i, ll ad, ll cf, ll l, ll r)//区间修改
{
    if (p[i].l == l && p[i].r == r)
    {
        p[i].v += ad;
        p[i].ad += ad;
        return;
    }
    push_down(i);
    ll i1 = i << 1, i2 = i << 1 | 1;
    if (l <= p[i1].r)
    {
        udq(i1, ad, cf, l, min(p[i1].r, r));
    }
    if (r >= p[i2].l)
    {
        udq(i2, ad, cf, max(p[i2].l, l), r);
    }
    p[i].v = max(p[i << 1].v, p[i << 1 | 1].v);
}
ll query(ll i, ll l, ll r)
{
    ll ans = LLONG_MIN;
    if (p[i].l == l && p[i].r == r)
    {
        ans = p[i].v;
        return ans;
    }
    push_down(i);
    ll i1 = i << 1, i2 = i << 1 | 1;
    ll l1,r1;
    if (l <= p[i1].r)
    {
        ans = max(ans, query(i1, l, min(p[i1].r, r)));
    }
    if (r >= p[i2].l)
    {
        ans = max(ans, query(i2, max(l, p[i2].l), r));
    }
    p[i].v=max(p[i<<1].v,p[i<<1|1].v);
    return ans;
}
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = x * 10 + ch - '0', ch = getchar();
    return x * f;
}
vector<ll>g[200005];
ll gs = 0;
ll b[300005];
ll vis[200005];
ll a[200005];
pair<ll, ll>ans[200005];
void dfs(ll x, ll fa)
{
    gs++;
    ll k=gs;
    for (auto j : g[x])
    {
        if (j == fa)continue;
        dfs(j, x);
    }
    ans[x]={k,gs};
}

void fio()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
}
int main()
{
    // fio();
    ll t;
    t = read();
    for (ll io = 1; io <= t; io++)
    {
        gs = 0;
        cout << "Case #" << io << ":" << endl;
        ll n, m;
        n = read();
        m = read();
        build(1, 1, n);
        for (ll i = 0; i <= n; i++)g[i].clear(),vis[i] = 0;
        for (ll i = 1; i <= n - 1; i++)
        {
            ll l, r;
            l = read();
            r = read();
            g[l].push_back(r);
            g[r].push_back(l);
        }
        ll cnt = 0;
        dfs(0, -1);
        for (ll i = 0; i <= n - 1; i++)
        {
            ll x;
            x = read();
            a[i] = x;
            udq(1, x, 0, ans[i].first, ans[i].second);
        }
        while (m--)
        {
            ll op;
            op = read();
            if (op == 0)
            {
                ll x, y;
                x = read();
                y = read();
                udq(1, y-a[x], 0, ans[x].first, ans[x].second);
                a[x] = y;
            }
            else
            {
                ll x;
                x = read();
                cout << query(1, ans[x].first, ans[x].second) << endl;
            }
        }
    }
}

Puzzled Elena

https://acm.hdu.edu.cn/showproblem.php?pid=5468
题面:
由于Stefan和Damon都爱上了Elena,而她很难做出选择。她的最好朋友Bonnie建议她向他们提出一个问题,然后她会根据谁能解决这个问题来做出选择。

假设有一棵树,有n个顶点和n-1条边,每个顶点都有一个值。根是顶点1。那么对于每个顶点,你能告诉我它的子树中有多少个顶点可以被认为是与它互质的吗?
注:如果两个顶点的值的最大公约数(GCD)等于1,则称这两个顶点是互质的
输入:
有多个测试(不超过8个)。
对于每个测试,第一行有一个数字n(1≤n≤105),之后有n-1行,每行有两个数字a和b(1≤a,b≤n),表示顶点a与顶点b相连。然后下一行有n个数字,第i个数字表示第i个顶点的值。顶点的值不低于1且不超过105。
输出:
对于每个测试,首先请输出“Case #k: ”,其中k是测试的编号。然后,请输出一行包含n个数字(用空格分隔),代表每个顶点的答案。
样例:
5
1 2
1 3
2 4
2 5
6 2 3 4 5
——————
Case #1: 1 1 0 0 0
感受:某个题集说是dfs序+容斥,感觉就是普通dfs+容斥。和dfs序运用联系不强。具体坐下来就感受到是根据dfs遍历下来的性质进行容斥而已。就是先预处理质因数的所有子集,具体容斥方法可以参考的上一篇博客的G题,然后正常dfs下来,进来先容斥一遍算互质的数的个数,回溯再再算一次容斥的数的个数。
两者相减即可,注意是多组输入。欧拉筛+容斥+dfs
https://www.cnblogs.com/cjcf/p/18551489

#include<iostream>
	#include<queue>
	#include<map>
	#include<set>
	#include<stdio.h>
	#include<cstdio>
	#include<vector>
//	#include<bits/stdc++.h>
	#include<algorithm>
	#include<deque>
	#include<cctype>
	#include<string.h>
	#include<math.h>
	#include<time.h>
	// #include<random>
	#include<stack>
	#include<string>
	#define ll                                int
	#define lowbit(x) (x & -x)
	#define endl "\n"//                           交互题记得删除
	using namespace std;
	// mt19937 rnd(time(0));
	const ll mod = 998244353;
	const ll maxn = 1e5+5;
	void fio()
	{
		ios::sync_with_stdio(0);
		cin.tie(0);
		cout.tie(0);
	}
	ll h[120000];
	ll ne[120000<<1];
	ll to[100002<<1];
	ll cnt=0;
	ll a[120000];
	bool vi[120000];
	ll z[100002][100];
	void add(ll l,ll r)
	{
		to[cnt]=r;
		ne[cnt]=h[l];
		h[l]=cnt++;
	}
	ll b[120000];
	ll gs=0;
	ll tj[120000];
	ll cf=0;
	ll shu[120000];
	ll ut[100002];
	ll rt[100002];
	void dfs(ll x,ll fa)
	{
		ll gx=0;
		for(ll i=1;i<=rt[x];i++)
		{
			if(ut[z[x][i]]%2)
			gx+=tj[z[x][i]];
			else 
			gx-=tj[z[x][i]];
			tj[z[x][i]]++;
		}
		ll uo=cf-gx;//互质数
		cf++;
		for(ll i=h[x];i>=0;i=ne[i])
		{
			if(to[i]==fa)continue;
			dfs(to[i],x);
		}
		gx=0;
		ll op=0;
		for(ll i=1;i<=rt[x];i++)
		{
			if(ut[z[x][i]]%2)
			gx+=tj[z[x][i]];
			else 
			gx-=tj[z[x][i]];
		}
		op=cf-gx;
		shu[x]=op-uo;
	}
	
	bool st[120000];
	void ola(ll x)
	{
		for(ll i=2;i<=x;i++)
		{
			if(!st[i])gs++,b[gs]=i,vi[b[gs]]=1;
			for(ll j=1;b[j]<=x/i;j++)
			{
				st[b[j]*i]=1;
				if(i%b[j]==0)break;
			}
		}
	}
	int main()
	{
		fio();
		ll t;
		ll n;
		ll io=0;
		ola(100000);
		while(cin>>n)
		{
			io++;
			cf=0;
			cnt=0;
			cout<<"Case #"<<io<<": ";
			for(ll i=1;i<=n;i++)h[i]=-1;
			for(ll i=1;i<=n-1;i++)
			{
				ll l,r;
				cin>>l>>r;
				add(l,r);
				add(r,l);
			}
			ll d[150];
			for(ll i=1;i<=n;i++)
			{
				cin>>a[i];
				ll u=a[i];
				ll fo=0;
				for(ll k=1;k<=gs;k++)
				{
					if(u%b[k]==0)
					{
						while(u%b[k]==0)
						{
							u/=b[k];
						}
						fo++;
						d[fo]=b[k];
					}
					if(u==1)
					break;
					if(vi[u])
					{
						fo++;
						d[fo]=u;
						break;
					}
				}
				ll uo=0;
				for(ll k=1;k<=(1ll<<fo)-1;k++)
				{
					ll co=0;
					ll ans=1;
					for(ll j=0;j<=fo-1;j++)
					{
						if((1ll<<j)&k)
						{
							co++;
							ans*=d[j+1];
						}
					}
					uo++;
					z[i][uo]=ans;
					ut[ans]=co;
				}
				rt[i]=uo;
			}
			dfs(1,-1);
			for(ll i=1;i<=n;i++)
			cout<<shu[i]<<" ";
			cout<<endl;
		}
	}
posted @ 2024-11-20 01:26  长皆  阅读(39)  评论(0编辑  收藏  举报