0%

问题描述:

我们定义某天的最小波动值为$min \lbrace |$该天以前某一天的营业额$-$该天营业额$| \rbrace $

第一天的最小波动值为第一天的营业额

你的任务是编写一个程序帮助Tiger来计算这一个值:每一天的最小波动值之和。

输入:

第一行为正整数$n(n<=32767)$,表示该公司从成立一直到现在的天数,接下来的$n$行每行有一个整数$ai(|ai|<=1000000)$,表示第$i$天公司的营业额,可能存在负数

输出:

仅一个整数,即每一天的的最小波动值之和(结果小于$2^{31}$)

思路:

我们就是要实现 动态插点,找前驱和后继 这三种操作。首先平衡树肯定是能够做到的,于是就码了一发Splay。

然后其实权值线段树也可以做到。我们先将这些值离散化,每个值对应一个叶子节点。于是我们对于每一个节点维护区间内最左边和最右边的位置就好了。

代码:

Splay代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <bits/stdc++.h>
#define MAXN 100005
#define INF 1e9
using namespace std;

int n, m, x, root, tot, ans;

struct Node
{
int son[2], fa, val, size, tag;
void init(int x, int y)
{
son[0]=son[1]=0;
size=1; val=x; fa=y;
}
} t[MAXN];

void up(int x)
{
t[x].size=t[t[x].son[0]].size+t[t[x].son[1]].size+1;
}

void rotate(int x)
{
int y=t[x].fa, z=t[y].fa;
int k1=t[y].son[1]==x, k2=t[z].son[1]==y;
t[z].son[k2]=x; t[x].fa=z;
t[y].son[k1]=t[x].son[k1^1]; t[t[x].son[k1^1]].fa=y;
t[x].son[k1^1]=y; t[y].fa=x;
up(y); up(x);
}

void splay(int x, int k)
{
while(t[x].fa!=k)
{
int y=t[x].fa, z=t[y].fa;
if(z!=k)
{
if((t[z].son[1]==y)^(t[y].son[1]==x)) rotate(x);
else rotate(y);
}
rotate(x);
}
if(k==0) root=x;
}

int findnext(int k, int op)
{
int x=root;
x=t[x].son[op];
while(t[x].son[op^1]) x=t[x].son[op^1];
return x;
}

void insert(int k)
{
int fa=0, x=root;
while(x) fa=x, x=t[x].son[k>t[x].val];
x=++tot;
if(fa) t[fa].son[k>t[fa].val]=x;
t[x].init(k, fa);
splay(x, 0);
}

int main()
{
insert(-INF);
insert(INF);
scanf("%d%d", &n, &x);
insert(x);
ans+=x;
for(int i=2; i<=n; i++)
{
scanf("%d", &x);
insert(x);
int y=t[findnext(x, 0)].val;
int z=t[findnext(x, 1)].val;
ans+=min(x-y, z-x);
}
printf("%d\n", ans);
}

权值线段树代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAXN 33000
#define mid (l+r>>1)
#define ls root<<1
#define rs root<<1|1
#define INF 1e9
using namespace std;

int n, ans, a[MAXN], b[MAXN], vis[MAXN], maxpos[MAXN*4], minpos[MAXN*4];

void up(int root)
{
maxpos[root]=max(maxpos[ls], maxpos[rs]);
minpos[root]=min(minpos[ls], minpos[rs]);
}

void update(int root, int l, int r, int x)
{
if(l>x || r<x) return ;
if(l==r) {maxpos[root]=minpos[root]=l; return;}
update(ls, l, mid, x);
update(rs, mid+1, r, x);
up(root);
}

int queryl(int root, int l, int r, int x, int y)
{
if(l>y || r<x) return 0;
if(l>=x && r<=y) return maxpos[root];
return max(queryl(ls, l, mid, x, y), queryl(rs, mid+1, r, x, y));
}

int queryr(int root, int l, int r, int x, int y)
{
if(l>y || r<x) return n+1;
if(l>=x && r<=y) return minpos[root];
return min(queryr(ls, l, mid, x, y), queryr(rs, mid+1, r, x, y));
}

int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
b[i]=a[i];
}
b[0]=-INF; b[n+1]=INF;
for(int i=0; i<MAXN*4; i++) minpos[i]=n+1;

sort(b+1, b+n+1);
unique(b+1, b+n+1);
for(int i=1; i<=n; i++)
{
int pos=lower_bound(b+1, b+n+1, a[i])-b;
if(vis[pos]) continue;
vis[pos]=1;
update(1, 1, n, pos);

if(i==1) ans+=a[i];
else
{
int x=queryl(1, 1, n, 1, pos-1);
int y=queryr(1, 1, n, pos+1, n);
ans+=min(a[i]-b[x], b[y]-a[i]);
}
}
printf("%d\n", ans);
}

8月30日 AtCoder Regular Contest 101 题目链接:https://arc101.contest.atcoder.jp/

C - Candles

思路: 比较水的一题,发现点燃的$k$根蜡烛一定是相邻的。于是我们之间枚举k根蜡烛的位置就好了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define MAXN 100005
#define LL long long
#define INF 1e9
using namespace std;

int n, k, x[MAXN], ans=INF;

int main()
{
scanf("%d%d", &n, &k);
for(int i=1; i<=n; i++) scanf("%d", &x[i]);
for(int i=k; i<=n; i++)
{
if(x[i]<0) ans=min(ans, -x[i-k+1]);
if(x[i]>=0 && x[i-k+1]<=0) ans=min(ans, x[i]-x[i-k+1]+min(x[i], -x[i-k+1]));
if(x[i-k+1]>0) ans=min(ans, x[i]);
}
printf("%d\n", ans);
}

D - Median of Medians

思路: 好题。。

有一个很神奇的地方就是这题所求的Median of Medians是满足二分性的。首先我们有$n*(n+1)/2$个连续子串,我们设二分的值为$x$,如果至少有$n*(n+1)/4$个 中位数大于等于$x$的连续子串,那么答案就一定是大于等于$x$的,否则就是小于$x$,于是我们就可以根据这个进行二分。

接下来我们的问题就是如何求 中位数大于等于$x$的连续子串 的个数。我们可以将大于等于$x$的数视为1,将小于$x$的数视为-1,然后维护前缀和,记为$c[i]$。那么对于子串$[l, r]$,如果$c[l]<=c[r]$那么这个子串的中位数就是大于$x$的。对于符合要求$[l, r]$的个数,不难想到使用树状数组进行维护。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <map>
#define MAXN 100005
#define LL long long
using namespace std;

int n, l=1e9, r, ans, a[MAXN], b[MAXN], c[MAXN];
LL t[MAXN*2];

void add(int x, int y)
{
for(; x<=2e5; x+=(x&-x)) t[x]+=y;
}

LL ask(int x)
{
LL sum=0;
for(; x; x-=(x&-x)) sum+=t[x];
return sum;
}

bool check(int mid)
{
LL num=0;
memset(t, 0, sizeof(t));
for(int i=1; i<=n; i++)
{
if(a[i]<mid) c[i]=-1;
else c[i]=1;
c[i]=c[i]+c[i-1];
}
for(int i=0; i<=n; i++)
{
num+=ask(c[i]+1e5);
add(c[i]+1e5, 1);
}
return num>=1LL*(n+1)*n/4;
}

int main()
{
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
b[i]=a[i];
}
sort(b+1, b+n+1);
l=1; r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(b[mid])) ans=mid, l=mid+1;
else r=mid-1;
}
printf("%d\n", b[ans]);
}

E - Ribbons on Tree

思路: 也是一道很好的题目,是一个计数类的树形DP。

看完题目后就觉得像是一个计数类的DP,于是按照常规的计数类DP思考如何转化和如何设计状态。首先是可以用容斥的思想,$ans=$所有情况 $-$ 至少一条边未覆盖情况 $+$ 至少两条边未覆盖情况$…..$

那么问题就来了,如何计算 至少$k$条边未覆盖的情况数。如果我们知道$k$条未覆盖的边的具体位置,很容易想到这棵树被分为了$k+1$个互不连通的区域(这里是区域,不一定要联通)。对于一个含有$x$个节点的区域,这个区域的可能情况数量$g(x)$为: 那么如果我们知道了$k$条未覆盖的边的具体位置,这种情况的方案数就是$g(x_1)*g(x_2)….*g(x_{k+1})$,$x_i$就是第$i$个区域的节点数。

但实际上我们并不知道$k$条未覆盖边的位置,并且枚举这$k$条边的复杂度也是不满足要求的,这时我们就可以换一个思路,利用树形DP(虽然思路变了,但是上面的结论都是要用到的)。我们设$f[i][j][k]$为在$i$的子树中,$i$所在联通块大小为$j$,未覆盖边数的奇偶性为$k$($k$为0或1),$j$可能为0,此时表示$i$所在联通块为任意大小。方程的转移就看代码好了,实在是讲不清楚。最后答案就是$f[1][0][1]-f[1][0][0]$

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN 5005
#define p 1000000007
#define LL long long
using namespace std;

int n, cnt, head[MAXN], size[MAXN];
LL f[MAXN][MAXN][2];

struct Edge {int next, to;} edge[MAXN*2];

void addedge(int from, int to)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
head[from]=cnt;
}

void dfs(int x, int fa)
{
size[x]=1; f[x][1][0]=1;
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(to==fa) continue;
dfs(to, x);
for(int j=size[x]+size[to]; j>=0; j--)
{
LL tx=f[x][j][0], ty=f[x][j][1];
f[x][j][0]=(f[to][0][0]*tx+f[to][0][1]*ty)%p;
f[x][j][1]=(f[to][0][0]*ty+f[to][0][1]*tx)%p;
for(int k=max(1, j-size[x]); k<=min(j, size[to]); k++)
{
f[x][j][0]=(f[x][j][0]+f[to][k][0]*f[x][j-k][0]%p+f[to][k][1]*f[x][j-k][1]%p)%p;
f[x][j][1]=(f[x][j][1]+f[to][k][0]*f[x][j-k][1]%p+f[to][k][1]*f[x][j-k][0]%p)%p;
}
}
size[x]+=size[to];
}
LL temp=1;
for(int i=2; i<=size[x]; i+=2)
{
temp=temp*(i-1)%p;
f[x][0][0]=(f[x][0][0]+f[x][i][1]*temp)%p;
f[x][0][1]=(f[x][0][1]+f[x][i][0]*temp)%p;
}
}

int main()
{
scanf("%d", &n);
for(int i=1; i<n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
addedge(y, x);
}
dfs(1, 0);
printf("%lld\n", (f[1][0][1]-f[1][0][0]+p)%p);
}

F - Robots and Exits

思路:

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN 100005
#define p 1000000007
#define LL long long
using namespace std;

int n, m, cnt, x[MAXN], y[MAXN], a[MAXN], b[MAXN];
LL t[MAXN];

struct Node {int a, b;} c[MAXN];

bool CMP(Node x, Node y)
{
if(x.a!=y.a) return x.a<y.a;
return x.b>y.b;
}

void add(int x, int y)
{
for(; x<=cnt; x+=(x&-x)) t[x]=(t[x]+y)%p;
}

LL ask(int x)
{
LL sum=0;
for(; x; x-=(x&-x)) sum=(sum+t[x])%p;
return sum;
}

int main()
{
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%d", &x[i]);
for(int i=1; i<=m; i++) scanf("%d", &y[i]);
for(int i=1, j=1; i<m && j<=n; i++)
{
while(x[j]<y[i]) j++;
while(x[j]>=y[i] && x[j]<=y[i+1])
{
a[++cnt]=x[j]-y[i];
b[cnt]=y[i+1]-x[j];
c[cnt].a=a[cnt]; c[cnt].b=b[cnt];
j++;
}
}
sort(a+1, a+cnt+1); sort(b+1, b+cnt+1);
for(int i=1; i<=cnt; i++)
{
c[i].a=lower_bound(a+1, a+cnt+1, c[i].a)-a;
c[i].b=lower_bound(b+1, b+cnt+1, c[i].b)-b;
}
sort(c+1, c+cnt+1, CMP);
for(int i=1; i<=cnt; i++)
if(c[i].a!=c[i-1].a || c[i].b!=c[i-1].b) add(c[i].b, (ask(c[i].b-1)+1)%p);
printf("%d\n", (ask(cnt)+1)%p);
}

问题描述:

给一棵树,每条边有权。求一条简单路径,权值和等于$K$,且边的数量最小。

输入:

第一行:两个整数$n,k$ 第二至 $n$行:每行三个整数,表示一条无向边的两端和权值 (注意点的编号从$0$开始)

输出:

一个整数,表示最小边数量 如果不存在这样的路径,输出$-1$

思路:

对于静态统计树上路径的问题,点分治往往是一种可行的做法。这道题也就可以用点分治来做。

分治时对于根节点$p$,可以开一个数组$val$记录从$p$开始每个长度的链最少需要多少条边。需要注意的是,我们应该对每个子树先dfs一遍更新答案,再dfs一遍更新$val$数组。这是因为如果同时操作,计算时就会乱掉(可能两条链属于同一棵子树,这样两条链就有重复的边)。dfs时记录当前链的深度$dep$和边数$num$,就用$val[k-dep]+num$来更新答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <algorithm>
#define MAXN 200005
#define MAXK 1000006
#define INF 0x3f3f3f3f
#define LL long long
using namespace std;

int n, k, cnt, tot, root, ans, head[MAXN], size[MAXN], val[MAXK], f[MAXN];
bool vis[MAXN];

struct Edge {int next, to, dis;} edge[MAXN*2];

void addedge(int from, int to, int dis)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
edge[cnt].dis=dis;
head[from]=cnt;
}

void getroot(int x, int fa)
{
size[x]=1; f[x]=0;
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(to==fa || vis[to]) continue;
getroot(to, x);
size[x]+=size[to];
f[x]=max(f[x], size[to]);
}
f[x]=max(f[x], tot-size[x]);
if(f[x]<f[root]) root=x;
}

void getdis(int x, int fa, int dep, int num, int op)
{
if(op==0) val[dep]=min(val[dep], num);
else val[dep]=INF;
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(to==fa || vis[to]) continue;
if(dep+edge[i].dis<=k) getdis(to, x, dep+edge[i].dis, num+1, op);
}
}

void update(int x, int fa, int dep, int num)
{
ans=min(ans, val[k-dep]+num);
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(to==fa || vis[to]) continue;
if(dep+edge[i].dis<=k) update(to, x, dep+edge[i].dis, num+1);
}
}

void divide(int x)
{
vis[x]=1; val[0]=0;
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(vis[to]) continue;
if(edge[i].dis<=k) update(to, x, edge[i].dis, 1);
if(edge[i].dis<=k) getdis(to, x, edge[i].dis, 1, 0);
}
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(vis[to]) continue;
if(edge[i].dis<=k) getdis(to, x, edge[i].dis, 1, 1);
}
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(vis[to]) continue;
tot=size[to]; root=0;
getroot(to, x);
divide(root);
}
}

int main()
{
f[0]=INF; ans=INF;
memset(val, 0x3f, sizeof(val));

scanf("%d%d", &n, &k);
for(int i=1; i<n; i++)
{
int x, y, z;
scanf("%d%d%d", &x, &y, &z);
x++; y++;
addedge(x, y, z);
addedge(y, x, z);
}
tot=n; root=0;
getroot(1, 0);
divide(root);
if(ans==INF) printf("-1\n");
else printf("%d\n", ans);
}

8月18日 2018百度之星复赛 题目链接:http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=827

1001 - 没有兄弟的舞会

思路: 一个很简单的贪心吧。。。对于每一个节点选出它最大和最小的儿子(注意还可以不选),然后在没有选的里面贪心找一个最大和最小值加入答案。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define MAXN 100005
#define LL long long
#define LB long double
#define INF 1e18
using namespace std;

int T, n, cnt, head[MAXN], maxson[MAXN], minson[MAXN], maxtag[MAXN], mintag[MAXN];
LL maxans, minans, w[MAXN], maxval[MAXN], minval[MAXN];

struct Edge {int next, to;} edge[MAXN*2];

void addedge(int from, int to)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
head[from]=cnt;
}

void dfs(int x, int fa)
{
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(to==fa) continue;
dfs(to, x);
if(w[to]>maxval[x]) maxson[x]=to, maxval[x]=w[to];
if(w[to]<minval[x]) minson[x]=to, minval[x]=w[to];
}
maxans+=maxval[x];
minans+=minval[x];
maxtag[maxson[x]]=1;
mintag[minson[x]]=1;
}

int main()
{
scanf("%d", &T);
while(T--)
{
cnt=maxans=minans=0;
for(int i=0; i<MAXN-1; i++) head[i]=maxson[i]=minson[i]=maxval[i]=minval[i]=mintag[i]=maxtag[i]=0;
LL mintemp=0, maxtemp=0;
scanf("%d", &n);
for(int i=2; i<=n; i++)
{
int fa;
scanf("%d", &fa);
addedge(fa, i);
addedge(i, fa);
}
for(int i=1; i<=n; i++) scanf("%lld", &w[i]);
dfs(1, 0);
if(w[1]>0) maxtag[1]=1, maxans+=w[1];
if(w[1]<0) mintag[1]=1, minans+=w[1];
for(int i=1; i<=n; i++)
{
if(!mintag[i]) mintemp=min(mintemp, w[i]);
}
for(int i=n; i>=1; i--)
{
if(!maxtag[i]) maxtemp=max(maxtemp, w[i]);
}
printf("%lld %lld\n", maxans+maxtemp, minans+mintemp);
}
}

1002 - 序列期望

思路: 一道蛮好的期望题。我们可以先枚举最大值$h$,然后计算最大值为$h$时对答案的贡献。最大值恰好为$h$是的贡献不方便计算,我们可以先计算最大值小于等于$h$和最大值小于$h$的期望,利用一个容斥相减就好了。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define MAXN 100005
#define LL long long
#define LB long double
#define INF 1e18
#define p 1000000007
using namespace std;

int T, n, l[MAXN], r[MAXN], tag, lmax, rmax;

LL x[MAXN], y[MAXN];

LL ksm(LL x, LL y)
{
LL sum=1;
while(y)
{
if(y&1) sum=(sum*x)%p;
y>>=1; x=(x*x)%p;
}
return sum;
}

LL cal(LL l, LL r) {return (l+r)*(r-l+1)/2%p;}

int main()
{
scanf("%d", &T);
while(T--)
{
lmax=rmax=0;
LL up=0, down=1;
scanf("%d", &n);
for(int i=1; i<=n; i++)
{
scanf("%d%d", &l[i], &r[i]);
lmax=max(lmax, l[i]);
rmax=max(rmax, r[i]);
}
for(int i=lmax; i<=rmax; i++)
{
tag=0;
LL sum1=1, sum2=1;
for(int j=1; j<=n; j++)
{
if(r[j]>=i) x[j]=1;
else x[j]=i-r[j]+1;
y[j]=i-l[j]+1;
}
for(int j=1; j<=n; j++) sum1=sum1*cal(x[j], y[j])%p;
for(int j=1; j<=n; j++)
{
if(x[j]==1) sum2=(sum2*cal(x[j]+1, y[j]))%p;
else sum2=(sum2*cal(x[j], y[j]))%p;
}
up=(up+sum1-sum2+p)%p;
}
for(int i=1; i<=n; i++) down=(down*(r[i]-l[i]+1))%p;
printf("%lld\n", up*ksm(down, p-2)%p);
}
}

1003 - 带劲的and和

思路: 首先观察式子,就可以发现我们可以对每一个联通块分开维护。对于每一个联通块里面的权值我们不难想到$v_i&v_j$可以按位来维护,对于每一位分别记录有多少个数字在这一位上出现过。然后$max(v_i, v_j)$可以比较巧妙的先将权值排序进行维护,从小到大依次按顺序计算贡献。大概的思路就是这样,详细的看代码吧(懒)

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define MAXN 100005
#define LL long long
#define LB long double
#define INF 1e18
#define p 1000000007
using namespace std;

int T, n, m, cnt, top, sta[MAXN], head[MAXN], vis[MAXN];

LL ans, w[MAXN], num[50];


struct Edge {int next, to;} edge[MAXN*2];

void addedge(int from, int to)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
head[from]=cnt;
}

bool CMP(int x, int y) {return w[x]<w[y];}

void dfs(int x)
{
vis[x]=1; sta[++top]=x;
for(int i=head[x]; i; i=edge[i].next)
{
int to=edge[i].to;
if(vis[to]) continue;
dfs(to);
}
}

int main()
{
scanf("%d", &T);
while(T--)
{
ans=cnt=0;
for(int i=0; i<MAXN-1; i++) head[i]=vis[i]=0;
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) scanf("%lld", &w[i]);
for(int i=1; i<=m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
addedge(x, y);
addedge(y, x);
}
for(int i=1; i<=n; i++)
{
if(vis[i]) continue;
top=0;
memset(num, 0, sizeof(num));
dfs(i);
sort(sta+1, sta+top+1, CMP);
for(int j=1; j<=top; j++)
{
int temp=sta[j];
for(int k=31; k>=0; k--)
if((w[temp]>>k)&1) ans=(ans+w[temp]*num[k]%p*(1LL<<k))%p, num[k]++;
}
}
printf("%lld\n", ans);
}
}