2024 0130 模拟赛

image
image


做法:

  • \(n,q\) 范围均在 \(2000\) 以内,允许我们每一个询问都进行一次 \(dfs\) ,将 \(u\)\(v\) 之间的这条链拿出来,这样就转化成了一个链(一个序列)上的问题了。
  • o——o——o——o——o
  • 这是一条链
  • 我们设最左端的点为 \(i\),最右端的点为 \(j\),其中的点为 \(i+1\)\(j-1\) 等等。
  • 观察题目中给的 \(dis(i,j)\) 的定义式,我们可以知道就是从 \(i~j\) 的点集中任意选取两个点,并且 \((i,j)\) 反过来的 \((j,i)\) 只算一遍。因此,我们可以得出这样一个等价于原式子的式子:

\[\operatorname{dis}(i,j)=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)-\operatorname{dis}(i,j) \]

  • (右边减去一个 \(\operatorname{dis}(i,j)\) 是因为枚举 \(l,r\) 时算到了)
  • 这样,有式子①:

\[2\operatorname{dis}(i,j)=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r) \]

  • 所以推:

\[\begin{aligned} 2\operatorname{dis}(i,j)&=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)\\ &=\sum\limits_{l=i}^j\left(\sum\limits_{l=i}^{j-1}\operatorname{dis}(l,r)+\operatorname{dis}(l,j)\right)\\ &=\sum\limits_{l=i}^j\sum\limits_{r=l+1}^{j-1}\operatorname{dis}(l,r)+\sum\limits_{l=i}^j\operatorname{dis}(l,j)\\ &=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+\sum\limits_{l=i+1}^j\operatorname{dis}(l,j)\\ &=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+\sum\limits_{l=i+1}^j\sum\limits_{r=l+1}^j\operatorname{dis}(l,r)-\sum\limits_{l=i+1}^j\sum\limits_{r=l+1}^{j-1}\operatorname{dis}(l,r)\\ &=2\operatorname{dis}(i,j-1)+\operatorname{dis}(i,j)+2\operatorname{dis}(i+1,j)-2\operatorname{dis}(i+1,j-1) \end{aligned} \]

  • (其中式子转换用到了前面的式子①)

  • 所以:

\[\operatorname{dis}(i,j)=2\operatorname{dis}(i,j-1)+2\operatorname{dis}(i+1,j)-2\operatorname{dis}(i+1,j-1) \]

  • 这样我们就得出了可以 \(O(1)\) 时间递推出来的 \(dis\) 值,所以我们考虑在询问时拿出来一条链的时候,直接递归地搜索这个式子。加上记忆化即可保证时间复杂度 \(O(n^2+nq)\)
  • 注意在使用式子①时,\(i,j\) 不能是相邻的点,所以在算 \(dis\) 时候的特判就是为了解决这个问题。可以看代码。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
namespace Light_Tea{void main();}
signed main(){
    Light_Tea::main();
    return 0;
}
namespace Light_Tea{
    void file(){}
    #define int64 long long

    const int N=2005;
    const int64 MOD=1e9+7;
    int n,q;
    
    struct Edge{
        int v,w,nxt;
    }e[N*2];
    int head[N],cnt_e=0;
    void add_edge(int u,int v,int w){e[++cnt_e]=(Edge){v,w,head[u]}, head[u]=cnt_e;}

    int fa[N],fro[N],a[N];
    int64 dis[N][N]; bool vis[N][N];
    void dfs(int u,int _fa,int _fro){
        fa[u]=_fa; fro[u]=_fro;
        for(int i=head[u], v,w; v=e[i].v, w=e[i].w, i; i=e[i].nxt){
            if(v==_fa) continue;
            dfs(v,u,w);
        }
    }
    int64 query(int l,int r){
        if(vis[a[l]][a[r]]) return dis[a[l]][a[r]];
        vis[a[l]][a[r]]=vis[a[r]][a[l]]=1;
        if(r-l==2){
            dis[a[l]][a[r]]=dis[a[r]][a[l]]=(dis[a[l]][a[r-1]]+dis[a[l+1]][a[r]])%MOD;
        }else{
            int64 ans=0;
            if(!vis[a[l+1]][a[r]]) ans=(ans+(query(l+1,r)*2)%MOD)%MOD;
            else ans=(ans+(dis[a[l+1]][a[r]]*2)%MOD)%MOD;
            if(!vis[a[l]][a[r-1]]) ans=(ans+(query(l,r-1)*2)%MOD)%MOD;
            else ans=(ans+(dis[a[l]][a[r-1]]*2)%MOD)%MOD;
            if(r-l==3){
                ans=(ans-dis[a[l+1]][a[r-1]])%MOD;
            }else{
                if(!vis[a[l+1]][a[r-1]]) ans=(ans-(query(l+1,r-1)*2)%MOD)%MOD;
                else ans=(ans-(dis[a[l+1]][a[r-1]]*2)%MOD)%MOD;
            }
            dis[a[l]][a[r]]=dis[a[r]][a[l]]=(ans%MOD+MOD)%MOD;
        }
        return dis[a[l]][a[r]];
    }

    void main(){
        // freopen("c.in","r",stdin);
        // freopen("c.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n-1;i++){
            int u,v,w; scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w), add_edge(v,u,w);
            dis[u][v]=dis[v][u]=w; vis[u][v]=vis[v][u]=1;
        }
        for(int i=1;i<=n;i++) vis[i][i]=1;
        scanf("%d",&q);
        while(q--){
            int u,v; scanf("%d%d",&u,&v);
            dfs(u,0,0);
            int cnt=0;
            for(int now=v; now!=fa[u]; now=fa[now]) a[++cnt]=now;
            int64 ans=query(1,cnt);
            printf("%lld\n",ans);
        }
        // int cnt=0;
        // for(int i=1;i<=n;i++){
        //     for(int j=1;j<=n;j++) cnt+=vis[i][j];
        // }
        // printf("%d",cnt);
        // for(int i=1;i<=n;i++){
        //     for(int j=1;j<=n;j++){
        //         printf("dis[%d][%d] %lld\n",i,j,dis[i][j]);
        //     }
        // }
    }
}

鸣谢 \(lnlmz\) 小朋友提供 \(\LaTeX\) 指导与大力帮助。

posted @ 2024-01-30 17:36  lnbyn  阅读(64)  评论(2)    收藏  举报