首先跑一个最短路处理出每个点到源点距离(路径边权最大值,没有卡SPFA),然后按照这个距离排序,然后依次划分层次,我们可以观察到点的种类最多600个,那么就将困难值按愉悦值分段,先按点权排序,再跑一遍O(n)逐个标记就行
以上就是预处理阶段
然后解答询问就非常愉快了,直接枚举每一段的起点,然后将求解区间与该段的交集大小乘以每段的愉悦值相加就行了,复杂度是最短路SPFA:O(kn),排序O(nlogn),求解O(qmax(c)),总复杂度就是O(kn+nlogn+qmax(x)),能过掉这个题
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=1000005; ll n,m,qq,x,opt; struct node{ ll w,v,lst; }t[N]; struct node2{ ll dis,id; }d[N]; ll dis[N],head[N],tot,c[N],tott,h[N],M,ans,sum=-1,p[N]; queue<ll>q; bool vis[N],vi[N]; void add(ll u,ll v,ll w){ t[++tot].v=v;t[tot].w=w;t[tot].lst=head[u];head[u]=tot; } void spfa(){//最短路 for(int i=1;i<=n;i++) { d[i].dis=1e9+1; } d[x].dis=0; q.push(x); while(!q.empty()) { ll u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i;i=t[i].lst) { ll v=t[i].v,w=t[i].w; if(d[v].dis>max(d[u].dis,w)) { d[v].dis=max(d[u].dis,w); if(vis[v]) continue; q.push(v); vis[v]=true; } } } } bool cmp(node2 x,node2 y){ return x.dis<y.dis; } void init(){//分段 sort(d+1,d+n+1,cmp); for(int i=1;i<=n;i++) { if(vi[c[d[i].id]]==false) { vi[c[d[i].id]]=true; tott++; if(d[i].dis>sum) { sum=d[i].dis; h[++h[0]]=d[i].dis; } p[h[0]]=tott; } } } void solve(ll l,ll r){//求解询问 ll t=l; ans=0; for(int i=1;i<=h[0];i++) { if(h[i]>r) { ans+=(r-t+1)*(p[i-1]); break; } if(t<=h[i]) { ans+=(h[i]-t)*(p[i-1]); t=h[i]; } } if(r>=h[h[0]]) { ans+=(r-t+1)*tott; } printf("%lld\n",ans); } int main() { ll xx,y,w,l,r; scanf("%lld%lld%lld%lld%lld",&n,&m,&qq,&x,&opt); if(opt==1) { scanf("%lld",&M); } for(int i=1;i<=n;i++) { scanf("%lld",&c[i]); d[i].id=i; } for(int i=1;i<=m;i++) { scanf("%lld%lld%lld",&xx,&y,&w); add(xx,y,w); add(y,xx,w); } spfa(); init(); for(int i=1;i<=qq;i++) { scanf("%lld%lld",&l,&r); if(opt==1) { l=(l^ans)%M+1; r=(r^ans)%M+1; } if(l>r) swap(l,r); solve(l,r); } }
顺便吐槽一下大样例太水
本人比赛时上面区间(r-t)忘了减一,水过了大样例,结果0分。。。。。。