首先提出结论:此题进行最小生成树之后结果不变

靠着这个结论,我们显然可以在最小生成树上以 为根遍历,处理出到每个节点的最大边权,进而求出到每一种类型的节点最小的最大边权。
题意要求我们求从 图片说明图片说明 中每一种最大边权可获得的类型总数,由于节点种类数较少,考虑对于每个节点最小所需的边权排序,然后按照每一种最大边权可以获得的节点分类,统计最后答案。

接着证明上述结论。考虑 Kruskal 算法中的贪心思想,假设前 条边都已考虑完毕,设第 条边连接 图片说明图片说明 两个集合(树),则当前如果有另外的边能够连接此二集合,肯定会边权更大,在 图片说明图片说明 两个集合统计答案时答案不会更优。再者两个集合内已经被考虑到的边边权一定小于当前边,目前在两个集合之间统计答案时最大的边权一定是当前边。
至此,我们知道了 Kruskal 算法中每一步都满足本题的贪心性质,用该算法求解最小生成树正确。

AC code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,q,sx;
bool opt;
const int N=500003,C=603;
int c[N],M;
int h[N],to[2*N],nxt[2*N];
ll val[2*N];
int tot=1;
int f[N];
ll d[N];
inline ll read(){
    ll xx=0,f=1;
    char c;
    while (!isdigit(c=getchar())) if (c=='-') f=-f;
    do xx=(xx<<1)+(xx<<3)+(c^48);
    while (isdigit(c=getchar()));
    return xx;
}
struct edge{
    int u,v;
    ll w;
    bool operator <(const edge x)const{
        return w<x.w;
    }
}e[N];
int get(int x){
    if (x==f[x]) return x;
    return f[x]=get(f[x]);
}
void add(int u,int v,ll w){
    tot++;
    nxt[tot]=h[u];
    h[u]=tot;
    val[tot]=w;
    to[tot]=v;
}
void mst(){
    for (int i=1; i<=n; i++) f[i]=i;
    sort(e+1,e+m+1);
    for (int i=1; i<=m; i++){
        int u=e[i].u,v=e[i].v;
        ll w=e[i].w;
        if (get(u)==get(v)) continue;
        f[get(u)]=get(v);
        add(u,v,w);
        add(v,u,w);
    }
}
void calc(int cur,ll maxflow,int pre){
    d[c[cur]]=min(d[c[cur]],maxflow);
//    cout<<cur<<endl;
    for (int i=h[cur]; i; i=nxt[i]){
        int y=to[i];
        ll w=val[i];
        if (y==pre) continue;
        calc(y,max(w,maxflow),cur);
    }
}
ll query(int l,int r){
    int ls=upper_bound(d+1,d+C-2,l)-d-1;
    int rs=upper_bound(d+1,d+C-2,r)-d-1; //确定l与r的种类数
    if (ls==rs){
        return (ll)(r-l+1)*ls;
    }
    ll ans=0;
    ans+=(ll)(d[ls+1]-l)*ls;
    ans+=(ll)(r-d[rs]+1)*rs;
    for (int i=ls+1; i<rs; i++){ //以种类数分类统计答案,时间O(600)
        ans+=(ll)(d[i+1]-d[i])*i;
    }
    return ans;
}
int main(){
//    freopen("graph.in","r",stdin);
//    freopen("graph.ans","w",stdout); 
    n=read(); m=read(); q=read(); sx=read(); opt=read();
    if (opt) M=read();
    for (int i=1; i<=n; i++) c[i]=read();
    for (int i=1; i<=m; i++) e[i]=(edge){(int)read(),(int)read(),read()};
    memset(d,0x7f,sizeof(d));
    mst(); //Kruskal 最小生成树
    calc(sx,0,0); //树上O(n)统计
    sort(d+1,d+C-2);
    ll lans=0;
    for (int i=1; i<=q; i++){
        ll l=read(),r=read();
        if (opt) l=(l^lans)%M+1,r=(r^lans)%M+1;
        if (l>r) swap(l,r);
        lans=query(l,r);
        printf("%lld\n",lans); //考场上lld没打丢了30pts
    }
    return 0;
}