\(\mathbb{Description}\)
\(\mathbb{Solution}\)
定义 \(to[i][j][k]\) 为 \(i\) 到 \(j\) 的路径长度为 \(2^k\) 的方案总数,\(dp[i][j]\) 为从 \(1\) 到 \(i\) 经过了 \(k\) 段的方案数。
我们将 \(a_i\) 分解成 \(2\) 的次方的和,这就是之前说的“段”。
最后我们知道从 \(1\) 到 \(i\) 经过了 \(k\) 段相当于从 \(i\) 到 \(1\) 经过了 \(k\) 段。考虑求魔法值的过程相当于一个从下到上不断异或的过程,我们只用将起点的初始魔法值异或起来即可。需要注意的是,如果一个点有偶数种方法到达 \(1\) 点,那么自己的魔法值就会抵消,所以要么异或要么不异或即可。
复杂度 \(O(q*n^2*31)=O(31000000)\)。
还是把之前的内容留在这里吧……表述很烂,而且还有问题,所以不要点开😀。
\(\text{Method 1}\):直接 \(\mathtt{dp}\)
可以考虑每个城市对首都魔法值的贡献:计算出城市 \(i\) 花费 \(t\) 天到达首都的方案数(魔法值的传播可以看作点的移动),只有方案数为奇时才能对 \(\rm ans\) 有异或 \(f_i\) 的贡献。但每次计算就太慢了,我们考虑倍增,令 \(\text{to}_{i,j,k}\) 为从 \(i\) 到 \(j\) 花费 \(2^k\) 的方案数,那么之后单次 \(\mathtt{dp}\) 就降到 \(\mathcal O(n^2\log a)\) 的复杂度,总共 \(\mathcal O(n^3\log a)\),可以通过此题。
\(\text{Method 2}\):矩阵乘法
朴素快速幂就懒得说了,这题学到的东西是转移矩阵可以预处理。复杂度和上面是一样的,但是我懒,所以实现非常粗糙。真的好水啊雾。
另外还有一个矩阵需要注意的点:无论在区间合并中(比如线段树维护矩阵),还是在倍增实现转移矩阵时,都需要满足分配律,比方后者,实际上就是证明
可以发现,这实际上就是证明 \((a\oplus b)\cdot c=ac\oplus bc\).
\(\mathbb{Code}\)
\(\text{Method 1}\)
#include <cstdio>
#include <cstring>
typedef long long ll;
const int N = 105;
int n, m, q, cnt, to[N][N][36], dp[N][36], s[36];
ll ans, f[N];
ll read() {
ll x = 0, f = 1; char s;
while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
return x * f;
}
int main() {
ll u, v;
n = read(), m = read(), q = read();
for(int i = 1; i <= n; ++ i) f[i] = read();
for(int i = 1; i <= m; ++ i) {
u = read(), v = read();
to[u][v][0] = to[v][u][0] = 1;
}
for(int p = 1; p <= 31; ++ p)
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
to[i][j][p] += to[i][k][p - 1] * to[k][j][p - 1];
while(q --) {
u = read(); cnt = 0; ans = 0; memset(dp, 0, sizeof dp);
for(int i = 31; ~i; -- i)
if((1ll << i) <= u) u -= (1ll << i), s[++ cnt] = i;
dp[1][0] = 1;//只在 1 号点设置值就好啦
for(int k = 1; k <= cnt; ++ k)
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
dp[i][k] += dp[j][k - 1] * to[j][i][s[k]];
for(int i = 1; i <= n; ++ i) {
dp[i][cnt] &= 1;
if(dp[i][cnt]) ans ^= f[i];
}
printf("%lld\n", ans);
}
return 0;
}
\(\text{Method 2}\)
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' || s<'0')
f |= (s=='-');
while(s>='0' && s<='9')
x = (x<<1)+(x<<3)+(s^48),
s = getchar();
return f?-x:x;
}
template <class T>
inline void write(T x) {
static int writ[50],tp=0;
if(x<0) putchar('-'),x=-x;
do writ[++tp] = x-x/10*10, x/=10; while(x);
while(tp) putchar(writ[tp--]^48);
}
# include <cstring>
typedef long long ll;
const int maxn = 105;
int n,m,q;
ll f[maxn];
struct mat {
ll a[maxn][maxn];
mat() { memset(a,0,sizeof a); }
mat operator * (const mat& t) const {
mat r;
for(int i=0;i<n;++i) for(int j=0;j<n;++j)
if(a[i][j]) for(int k=0;k<n;++k)
r.a[i][k] ^= a[i][j]*t.a[j][k];
return r;
}
} tra[32],ans;
int main() {
n=read(9), m=read(9), q=read(9);
for(int i=1;i<=n;++i) f[i]=read(9ll);
for(int i=1;i<=m;++i) {
int u=read(9)-1, v=read(9)-1;
tra[0].a[u][v] = tra[0].a[v][u] = 1;
}
for(int i=1;i<=31;++i) tra[i] = tra[i-1]*tra[i-1];
while(q --) {
ll s=read(9ll);
for(int i=0;i<n;++i) ans.a[0][i]=f[i+1];
for(int i=31; ~i; --i)
if(s>>i&1) ans = ans*tra[i];
print(ans.a[0][0],'\n');
}
return 0;
}
浙公网安备 33010602011771号