题目
n(n<=50)个点m(m<=300)条边保证联通的图,没有自环和重边
q(q<=1e7)次询问,每次给定一个整数x(x<=1e8),
询问当m条边的边权是abs(wi-x)时,图的最小生成树的值是多少
允许离线,最后只需将总共q个询问的答案异或在一起,输出最终的异或值
思路来源
jiangly代码
题解
当时赛中a了,但是写的很麻烦,现在补一下这个一次函数的写法
m条边枚举两两中点,C(m,2)+m个临界值变化的位置,去不去重无所谓
这m^2临界位置构成了若干个一次函数直线,询问两个临界为之间的时候,视为落在左边的直线上
那么就离线一下询问,双指针过一下这m^2个临界位置和q个询问,
对于每个询问x,找到不超过x的最大临界位置,
对其暴力做最小生成树求权值即可,
做好之后是一个形如y=coef*x+sum的形式,也就是一次函数y=kx+b
这个两年前没太想明白,现在看了看感觉差不多
考虑大于的值的贡献是w-x[now],小于的贡献是x[now]-w,
其中x[now]是临界位置,q[i]是询问的值,q[i]严格大于x[now],且严格小于x[now+1]
那么x固定时,每一条边都是对应有(k∈(-1,1),b)的一个贡献,线性相加即可
那么由于q[i]>x[now],所以当x[now]处,生成树有多个时,
需要选择coef最小的那个,才能使得一次函数在q[i]处的值最小
有个没弄懂的地方,是中点只能是(e[i].w+e[j].w+1)/2,也就是向上取整
改成(e[i].w+e[j].w)/2就wa了
大概感性理解了一下,感觉是左闭右开的直线形式,
代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int N=55,M=305,S=M*M,K=1e7+10;
int t,n,m,par[N],x[S],cnt,q[K];
int p,k,a,b,c;
ll ans;
struct edge{
int u,v,w;
void rd(){
sci(u),sci(v),sci(w);
}
}e[M];
int find(int x){
return par[x]==x?x:par[x]=find(par[x]);
}
int main(){
sci(n),sci(m);
rep(i,1,m)e[i].rd();
x[cnt++]=0;
rep(i,1,m){
rep(j,i,m){
x[cnt++]=(e[i].w+e[j].w+1)/2;
}
}
sort(x,x+cnt);
scanf("%d%d%d%d%d",&p,&k,&a,&b,&c);
rep(i,0,p-1){
sci(q[i]);
}
rep(i,p,k-1){
q[i]=(1ll*q[i-1]*a+b)%c;
}
sort(q,q+k);
int now=-1;
ll coef=0,sum=0;
rep(i,0,k-1){
int las=now;
while(now+1<cnt && x[now+1]<=q[i]){
now++;
}
//printf("i:%d q:%d las:%d now:%d\n",i,q[i],las,now);
if(las<now){
coef=sum=0;
rep(j,1,n)par[j]=j;
sort(e+1,e+m+1,[&](edge &u,edge &v){
int w1=abs(u.w-x[now]),w2=abs(v.w-x[now]);
if(w1^w2)return w1<w2;
else return u.w>v.w; // w1=w2时 在x[now]处贡献相同 但是选大的w coef更小 一次函数y=kx+b x增至q[i]时 k减小 y减小
});
rep(j,1,m){
int u=find(e[j].u),v=find(e[j].v);
if(u==v)continue;
par[v]=u;
int w=e[j].w;
//w>x[now] +w-x[now]
//w<=x[now] +x[now]-w
//printf("i:%d j:%d u:%d v:%d w:%d\n",i,j,u,v,w);
if(w>x[now]){//q[i]介于x[now]与x[now+1]间 有多少个比x[now]大的,就是有多少个比q[i]大的,所以这么求coef
coef--;
sum+=w;
}
else{
coef++;
sum-=w;
}
}
}
//printf("i:%d q:%d coef:%lld sum:%lld add:%lld\n",i,q[i],coef,sum,coef*q[i]+sum);
ans^=coef*q[i]+sum;
}
ptlle(ans);
return 0;
}