Transform
Accepts: 47
Submissions: 187
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
给出n个整数, 对于一个整数x, 你可以做如下的操作若干次: + 令x的二进制表示为b31b30...b0, 你可以翻转其中一个位. + 令y是给出的其中一个整数, 你可以把x变为x⊕y, 其中⊕表示位运算里面的异或操作. 现在有若干整数对(S,T), 对于每对整数你需要找出从S变成T的最小操作次数.
输入描述
输入包含多组数据. 第一行有一个整数T (T≤20), 表示测试数据组数. 对于每组数据: 第一行包含两个整数n和m (1≤n≤15,1≤m≤105), 表示给出整数的数目和询问的数目. 接下来一行包含n个用空格分隔的整数a1,a2,...,an (1≤ai≤105). 接下来m行, 每行包含两个整数si和ti (1≤si,ti≤105), 代表一组询问.
输出描述
对于每组数据, 输出一个整数S=(i=1∑mi⋅zi) mod (109+7), 其中zi是第i次询问的答案.
输入样例
1 3 3 1 2 3 3 4 1 2 3 9
输出样例
10
Hint
3→4 (2次操作): 3→7→4 1→2 (1次操作): 1⊕3=2 3→9 (2次操作): 3→1→9
注意到x^a^b^c^d...=y即 a^b^c^d...=x^y所以直接宽搜结果,O(1)查询。
代码:
#pragma warning(disable:4996)
#include <iostream>
#include <functional>
#include <algorithm>
#include <cstring>
#include <vector>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <stack>
#include <deque>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define INF 0x3fffffffffffffff
const ll mod = 1e9 + 7;
const int maxn = 1e5 + 5;
int n, m;
int val[20], dis[1 << 17];
queue<int>qu;
void cal()
{
int i, j, k, s;
memset(dis, -1, sizeof(dis));
while (!qu.empty())qu.pop();
dis[0] = 0;
qu.push(0);
while (!qu.empty())
{
int k = qu.front();
qu.pop();
for (i = 0; i < 17; i++)
{
s = k ^ (1 << i);
if (dis[s] == -1)
{
dis[s] = dis[k] + 1;
qu.push(s);
}
}
for (i = 1; i <= n; i++)
{
s = k^val[i];
if (dis[s] == -1)
{
dis[s] = dis[k] + 1;
qu.push(s);
}
}
}
}
void solve()
{
int i, j, k;
int u, v;
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
{
scanf("%d", &val[i]);
}
cal();
ll ans = 0, res;
for (k = 1; k <= m; k++)
{
scanf("%d%d", &u, &v);
res = dis[u^v];
ans = (ans + (ll)(res*k)%mod) % mod;
}
printf("%lld\n", ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("i.txt", "r", stdin);
freopen("o.txt", "w", stdout);
#endif
int t;
scanf("%d", &t);
while (t--)
{
solve();
}
return 0;
}