2 条题解

  • 0
    @ 2026-8-13 14:41:32

    我们发现,这张图一定是若干个环,环上节点可能连接着一棵树。那么不妨去考虑每一个点跳到环上时对应的点,然后再动整个环,这样就做完了。

    但真的这么简单吗?

    我们定义一个节点 xx 到环的距离为 depxdep_x,那么令 k<max1in(depi)k<\max_{1\le i\le n}(dep_i),此时,对于 depx>kdep_x>kxx,我们就需要考虑上跳 kk 步后落在哪里,显然无法暴力跳(毕竟有个东西叫链套菊花),所以问题就转变为如何快速求树上所有节点的 kk 级祖先,这个问题显然可以用 st 表解决。

    我们再想:既然刚刚那个问题可以用 st 表,st 表又能在 O(nlogk)O(n\log k) 的复杂度内求出每一个数跳 kk 次后的结果,这里还满足每一个点跳 kk 次的路径是唯一的。那么我们就可以抛弃原本的所有想法,直接用 st 表解决原问题,时间复杂度 O(nlogk)O(n\log k)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int a[100005];
    int ed[100005];
    int ans[100005];
    int st[100005][32];
    int main(){
    	int n,k;
    	cin>>n>>k;
    	for(int i=1;i<=n;i++){
    		cin>>a[i];
    		ed[i]=i;
    	}
    	for(int i=1;i<=n;i++){
    		cin>>st[i][0];
    	}
    	for(int i=1;i<=30;i++){
    		for(int j=1;j<=n;j++){
    			st[j][i]=st[st[j][i-1]][i-1];
    		}
    	}
    	for(int i=30;i>=0;i--){
    		for(int j=1;j<=n;j++){
    			if(k>=(1<<i)){
    				ed[j]=st[ed[j]][i];
    				k-=(1<<i);
    			}
    		}
    	}
    	int mx=0;
    	for(int i=1;i<=n;i++){
    		ans[ed[i]]+=a[i];
    	}
    	for(int i=1;i<=n;i++){
    		mx=max(mx,ans[i]);
    	}
    	cout<<mx<<"\n";
    	for(int i=1;i<=n;i++){
    		if(ans[i]==mx){
    			cout<<i<<" ";
    		}
    	}
    	return 0;
    }
    
    • 0
      @ 2026-8-11 20:39:59

      题意

      nn 个点,每个点权值为 aia_i,与 sis_i 有一条单向边。进行 kk 次操作,每次每个点的权值都移动到 sis_i。问最后哪些点权值最大。

      分析

      kk 的范围很大,考虑优化到 log\log。定义 sti,jst_{i,j} 表示第 ii 个节点进行 2j2^j 轮操作后,他的权值移动到了哪个点,转移是 sti,j=ststi,j1,j1st_{i,j}=st_{st_{i,j-1},j-1}。然后遍历每个点,将操作次数拆分,找到最终传给了哪个节点。

      时间复杂度 Θ(nlogk)\Theta(n \log k)

      代码

      #include<bits/stdc++.h>
      using namespace std;
      using LL=long long int;
      const int N=1e5+5;
      int n,k,a[N],cnt[N],st[N][35];
      int main(){
          cin>>n>>k;
          for(int i=1;i<=n;++i)cin>>a[i];
          for(int i=1,crush;i<=n;++i)cin>>crush,st[i][0]=crush;
          int m=__lg(k);
          for(int j=1;j<=m;++j)for(int i=1;i<=n;++i)st[i][j]=st[st[i][j-1]][j-1];
          for(int i=1;i<=n;++i){
              int num=i,now=m,round=k;
              while(round){
                  if(round>=(1<<now))round-=(1<<now),num=st[num][now];
                  --now;
              }
              cnt[num]+=a[i];
          }
          int mx=0;
          for(int i=1;i<=n;++i)mx=max(mx,cnt[i]);
          cout<<mx<<"\n";
          for(int i=1;i<=n;++i)if(cnt[i]==mx)cout<<i<<" ";
      }
      

      结语

      这个游戏一定很好玩。

      • 1

      信息

      ID
      12615
      时间
      1000ms
      内存
      612MiB
      难度
      7
      标签
      递交数
      65
      已通过
      15
      上传者