给定 K K K,有 n n n 个点 m m m 条边,第 i i i 条边连接 u i , v i u_i, v_i ui,vi,有边权 c i c_i ci。
有 q q q 次询问,每次给出一个 x x x,若 c i ⊕ x < K c_i⊕x
n , m ≤ 1 0 5 , c i , x , K ≤ 1 0 9 n,m\le10^5,c_i,x,K\le10^9 n,m≤105,ci,x,K≤109
考虑对于每个 c i c_i ci,放哪些 x x x 是能使 c i ⊕ x < K c_i\oplus x
然后把 c i ⊕ x c_i\oplus x ci⊕x 当前位变成与 K K K 相同的,就在当前位 x ← c i ⊕ K x\gets c_i\oplus K x←ci⊕K。发现当 01trie 上一个点被打了标记后,它所对应的边在点下面是一直存在的。
所以我们可以遍历 01trie,每走到一个点就把该点的边加入到并查集中,然后就维护答案,回溯时就把加的边删掉。由于要有删边操作,使用可撤销并查集,具体实现就是用栈记录操作顺序,然后从栈顶依次把操作撤销,注意到不能用路径压缩,要用启发式合并。
时间复杂度 O ( n log n log V ) O(n\log n\log V) O(nlognlogV), V V V 是值域。
#include
using namespace std;
#define ll long long
const int N=1e5+1;
int n,m,Q,K,q[N];
long long sum,ans[N];
struct node
{
int u,v,w;
bool operator<(const node &a)const{
return w<a.w;
}
}e[N];
struct dsu
{
int fa[N],sz[N],cnt;
pair<int,int> s[N];
ll ans;
void init(){for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;}
ll calc(ll x){return 1ll*x*(x-1)/2;}
int find(int x){return fa[x]==x?x:find(fa[x]);}
void merge(int a,int b){
int x=find(a),y=find(b);
if(x==y) return;
if(sz[x]<sz[y]) swap(x,y);
ans-=calc(sz[x])+calc(sz[y]);
fa[y]=x;
sz[x]+=sz[y];
s[++cnt]=make_pair(x,y);
ans+=calc(sz[x]);
}
void del(){
int x=s[cnt].first,y=s[cnt].second;
cnt--;
ans-=calc(sz[x]);
sz[x]-=sz[y];
fa[y]=y;
ans+=calc(sz[x])+calc(sz[y]);
}
void DEL(int x){while(cnt>x) del();}
}dsu;
struct trie
{
int tr[31*N][2];
int cnt=1;
vector<int> bj[31*N][2];
ll ans[31*N];
void insert1(int w,int id)
{
int czn=w^K;
int rt=1;
for(int i=30;i>=0;i--){
int x=K>>i&1,t=w>>i&1,y=czn>>i&1;
if(x&&t) bj[rt][1].push_back(id);
if(x&&!t) bj[rt][0].push_back(id);
if(!tr[rt][y]) tr[rt][y]=++cnt;
rt=tr[rt][y];
}
}
int insert2(int w)
{
int rt=1;
for(int i=30;i>=0;i--){
int x=w>>i&1;
if(!tr[rt][x]) tr[rt][x]=++cnt;
rt=tr[rt][x];
}
return rt;
}
void getans(int rt)
{
ans[rt]=dsu.ans;
if(tr[rt][0]){
int now=dsu.cnt;
for(auto i:bj[rt][0]) dsu.merge(e[i].u,e[i].v);
getans(tr[rt][0]);
dsu.DEL(now);
}
if(tr[rt][1]){
int now=dsu.cnt;
for(auto i:bj[rt][1]) dsu.merge(e[i].u,e[i].v);
getans(tr[rt][1]);
dsu.DEL(now);
}
}
}tr;
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
cin.tie(0)->sync_with_stdio();
cin>>n>>m>>Q>>K;
dsu.init();
for(int i=1;i<=m;i++){
cin>>e[i].u>>e[i].v>>e[i].w;
tr.insert1(e[i].w,i);
}
for(int i=1;i<=Q;i++){
cin>>q[i];
q[i]=tr.insert2(q[i]);
}
tr.getans(1);
for(int i=1;i<=Q;i++) cout<<tr.ans[q[i]]<<"\n";
}