2020杭电多校6 1006A Very Easy Graph Problem血泪史

DOOOR
An undirected connected graph has n nodes and m edges, The i-th edge’s length is 2i. Each node i has a value ai, which is either 0 or 1. You need to calculate:

∑ i = 1 n ∑ j = 1 n d ( i , j ) × [ a i = 1 ∧ a j = 0 ] ∑_{i=1}^{n}∑_{j=1}^nd(i,j)×[a_i=1∧a_j=0] i=1nj=1nd(i,j)×[ai=1aj=0]

d(i,j) indicates the shortest distance between i and j. [ ] is the Iverson bracket. ∧ indicates 𝙰𝙽𝙳.

Because the answer may be too large, please output the answer modulo 109+7.

Input
The first line contains one integer T(1≤T≤8),indicating the number of test cases.

The second line contains two ingeters n,m(1≤n≤10^5, 1≤m≤2×10^5).

The third line contains n positive integers a_1,a_2,…,a_n(a_i=0 or 1) —— the value of the nodes.

The following m lines contain two ingeters u,v(1≤u,v≤n), and the i-th line represents the i-th undirected edge’s length is 2i, between node u and v.

The sum of n,m is no more than 2×105.

Output
Print a single integer—— the value of the answer modulo 109+7.

Sample Input
1
3 2
0 1 0
3 1
3 2

Sample Output
10

点分治非正解!!!!!

但是除了打最小生成树之外,只用改一个统计函数就行!!!

拿到马上想到这就是个点对统计,点分治标准范围!!本来以为可以改几行就A掉
但我就是我)
果然只打过一个板子的点分治漏洞百出,虽然直接抄了板子,但是改着改着就想错了一堆东西:
主要的问题其实是统计函数sigma!!!!!
当然手滑打错东西(i打成0,快速幂没膜)还是有

  • 题目看反
  • 容斥原理还是驾驭不好!!!!!
    • 点分治到底怎么分别统计的???
    • 集合里面存的到底应该存啥还是一写就模糊
    • 以为集合里面任意两个元素的权值是和的平方减平方和!!!!!(深受《联合权值》影响

点分治模板那种分法:每次统计过根的无重边路径,在这题就是行的通的,不用作其他任何特殊考虑,比如根自己要跟子树里面组合这种情况,已经包含了,只是当根就是端点的一种情况!!
因为这些路径就是由 根到包含他自己的子树中所有点的路径组成的集合 中的元素组合成的!所以根为端点时,也就是根到根的路径和其他路径组成的!
当然集合只是思考时抽象的概念
真正只用记录这个集合里的每条路径的在子树中的端点的bool值和长度就行,按bool值分成两个子集,元素权值为路径长,那么满足条件的路径的和就是两个子集中的元素组成的元组的权值和!算很简单,只用先把子集1的所有元素的权值和sum1算出来,然后对于子集2中的每个元素,他的贡献就是自己的 权值和*size(子集1)+sum1不就行了吗!!!!!

既然是血泪史那就把50行调试留着吧
真的只用改一个函数而已!!!!!!!!!!!!!!!!!!!!!!!!!

// 1006-3
#include <bits/stdc++.h>
using namespace std;
#define ha 1000000007
#define inv2 500000004
typedef long long ll;
int T, n, m, k, e, ne, ansfinal, root, sizenow, sz[200004], maxsonsize[200004],
    head[200004], fa[200004];
ll dis[200004];
bool isroot[200004];
bool id[200004];

ll pow(ll b) {
  // cout<<b<<" ";
  ll ans = 1, base = 2;
  while (b) {
    if (b & 1) ans *= base, ans %= ha;
    base *= base;
    base %= ha;
    b >>= 1;
  }
  // cout<<ans<<endl;
  return ans;
}
struct nowdis {
  ll dis;
  bool mark;
} tmpdis[200004];
struct node {
  int v, next;
  ll w;
} a[200001];
bool cmp1(nowdis x, nowdis y) { return x.mark < y.mark; }
struct edge {
  int u, v;
  int w;
  // bool operator <(const edge& b) { return d < b.d; }
} side[750 * 750 + 5];
bool cmp(edge x, edge y) {
  if (x.w < y.w) return 1;
  return 0;
}
void insert(int u, int v, int w) {
  a[++e].v = v;
  a[e].next = head[u];
  head[u] = e;
  a[e].w = w;

  // cout<<u<<" "<<v<<" "<<w<<endl;;
}
int getfa(int t) {
  if (fa[t] != t) fa[t] = getfa(fa[t]);
  return fa[t];
}
void lh(int x, int y) { fa[getfa(x)] = getfa(y); }
bool inone(int x, int y) {
  if (getfa(x) == getfa(y)) return 1;
  return 0;
}
void findrt(int u, int fa) {
  sz[u] = 1;
  maxsonsize[u] = 0;
  //每次都初始化了,所以不会被之前的影响
  for (int p = head[u]; p; p = a[p].next) {
    int v = a[p].v;
    if (v == fa || isroot[v]) continue;
    findrt(v, u);
    sz[u] += sz[v];
    maxsonsize[u] = max(maxsonsize[u], sz[v]);
  }
  maxsonsize[u] = max(maxsonsize[u], (sizenow - sz[u]));
  if (maxsonsize[u] < maxsonsize[root]) root = u;
}
void getdis(int u, int fa) {
  // cout<<u<<"uuu"<<endl;
  tmpdis[++tmpdis[0].dis].dis = dis[u], tmpdis[tmpdis[0].dis].mark = id[u];
  // cout << u<<"udis "<<tmpdis[0].dis<<"l id"<<id[u]<<dis[u]<<endl;
  //表达式与上一个点没关系,就可以放循环外面,
  for (int p = head[u]; p; p = a[p].next) {
    int v = a[p].v;
    if (isroot[v] || v == fa) continue;
    dis[v] = (dis[u] + a[p].w) % ha;
    getdis(v, u);
  }
}
ll sigma(int u, int disnow) {
  //计算u的向下子树中符合要求的路径数量
  tmpdis[0].dis = 0;  //不用一个个清零,记长度
  dis[u] = disnow;
  // cout<<disnow<<endl;
  getdis(u, 0);
  // cout<<u<<"end"<<endl;
  //   cout<<"----\n";
  int num = 0;
  ll sum = 0, sum0 = 0, sum1 = 0;  //,sqsum0=0,sqsum1=0;
  // cout<<tmpdis[0].dis<<endl;
  sort(tmpdis + 1, tmpdis + 1 + tmpdis[0].dis, cmp1);
  // cout<<"here\n";
  int pos = 0;
  for (int i = 1; i <= tmpdis[0].dis; i++) {
    //	cout<<i<<" ";
    if (tmpdis[i].mark == 0)
      sum0 += (tmpdis[i].dis % ha), sum0 %= ha, pos = i;
    else
      sum += (sum0 + pos * tmpdis[i].dis) % ha, sum %= ha;
    // cout<<i<<" "<<u<< "uuu "<< tmpdis[i].dis<<tmpdis[i].mark<<endl;
  }
  // cout<<sum<<endl;

  // sum+=usum;
  /*
  cout << u << " " << sum0 << " " << sum1 << endl;
  for (int i = 1; i <= tmpdis[0].dis; i++) {
          (tmpdis[i].mark ? sqsum1 : sqsum0) += (tmpdis[i].dis * tmpdis[i].dis)
  % ha, (tmpdis[i].mark ? sqsum1 : sqsum0) %= ha; cout<<tmpdis[i].dis<<"
  "<<tmpdis[i].mark<<endl; cout<<sqsum0<<" "<<sqsum1<<endl;
  }
  ll sum=(sum1+sum0);
  sum0=(sum0*sum0-sqsum0+ha)*inv2%ha;
  sum1=(sum1*sum1-sqsum1+ha)*inv2%ha;
  sum=(((sum*sum)-(sqsum1+sqsum0)+ha)*inv2%ha-(sum0+sum1)%ha+ha)%ha;
  cout << u << " " << sum0 << " " << sum1<<" "<<usum << endl;
  sum+=usum;*/
  // cout<<sum<<"sum"<<endl;
  return sum;
}
void divide(int rt) {
  // cout<<rt<<"rt "<<endl;
  isroot[rt] = 1;                                 // mark root
  ansfinal += sigma(rt, 0) % ha, ansfinal %= ha;  //过root可重路径
  ll ansv;
  // cout<<ans<<" "<<endl;
  for (int p = head[rt]; p; p = a[p].next) {
    int v = a[p].v;
    if (isroot[v]) continue;
    ansv = sigma(v, a[p].w);  //减掉s t都在一个子树中的路
    // cout<<v<<"vvv "<<ansv<<endl;
    ansfinal -= ansv % ha, ansfinal = (ansfinal + ha) % ha;
    sizenow = sz[v];  //要找G的子树的总大小
    root = 0;
    findrt(v, 0);  //减的时候就顺便分一个
    divide(root);
  }
}
void clr() {
  root = 0;
  e = 0;
  for (int i = 0; i <= n; i++) isroot[i] = 0, head[i] = 0, maxsonsize[i] = 0;
  for (int i = 1; i <= n; i++) fa[i] = i;
  ansfinal = 0;
}
int kruskal() {
  int numin = 0;
  for (int i = 1; i <= m; i++) {
    if (!inone(side[i].u, side[i].v)) {
      // cout<<i<<endl;
      lh(side[i].u, side[i].v);
      insert(side[i].u, side[i].v, pow(side[i].w));
      insert(side[i].v, side[i].u, pow(side[i].w));
      numin++;
      if (numin == n - 1) break;
    }
  }
}

int main() {
  cin >> T;
  while (T--) {
    cin >> n >> m;
    clr();
    int u, v, w;
    for (int i = 1; i <= n; i++) scanf("%d", &id[i]);
    for (int i = 1; i <= m; i++) {
      scanf("%d%d", &u, &v);
      side[i].u = u, side[i].v = v, side[i].w = i;
      //  2^i
    }
    sort(side + 1, side + 1 + m, cmp);
    kruskal();
    maxsonsize[0] = sizenow = n;
    root = 0;
    findrt(1, 0);
    divide(root);
    cout << ansfinal << endl;
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值