P6569 [NOI Online #3 提高組]魔法值--倍增

n<=100,很明顯矩陣存儲圖,設原圖爲A(n*n),每次fi都是前i-1轉移而來,f[i]=W(1*n)*A^i也就是長度爲i的邊所影響的節點。

圖上可達矩陣普通乘法是 意義:(aij)^k代表ij路徑長度爲k的條數。

本題爲異或W(1*n)^(A運算A.....),這個運算是什麼運算呢?和普通矩陣乘法有什麼區別?

ai*j代表有aij個wi相異或。根據異或運算的規則,偶數個形同的數異或相當於沒有參與運算。因此可以普通矩陣運算再算奇偶性,也可以如果進行普通矩陣運算,注意數據範圍,數據值將會很大。或者直接進行矩陣上的異或運算。

將每次的矩陣進行倍增後存儲,備用。

對每個詢問,進行矩陣快速冪運算,時間複雜度爲n*n*n*logn,總時間複雜是爲q*n*n*n*logn,只能得40分,還是超時。

因爲W爲1*n的矩陣,先進行w*A的運算,時間複雜度爲n*n,總複雜度變爲q*n*n*logn

參考代碼:

#include<bits/stdc++.h>
using namespace std;
const int N=110;
typedef long long ll;
ll w[N], A[35][N][N],dp[N][N];
ll n,m,q,u,v;
ll work(ll x) {
	ll ans=0;
	memset(dp,0,sizeof(dp));
	for(int i=1; i<=n; i++)dp[i][i]=1;
	for(int l=0; l<=31&&x; l++) {
		if(x&(1ll<<l)) {
			ll tmp[N][N]= {0};
			for(int i=1; i<=n; i++)
				for(int j=1; j<=n; j++)
					for(int k=1; k<=n; k++) {
						tmp[i][j]^=dp[i][k]*A[l][k][j];
					}
			memcpy(dp,tmp,sizeof(tmp));
			x-=(1<<l);
		}
	}
	for(int i=1; i<=n; i++)if(dp[i][1])ans=ans^w[i];
	return ans;
}
int main() {

	scanf("%lld%lld%lld",&n,&m,&q);
	for(int i=1; i<=n; i++)scanf("%lld",&w[i]);
	for(int i=1; i<=m; i++) {
		scanf("%lld%lld",&u,&v);
		A[0][u][v]=A[0][v][u]=1;
	}
	for(int l=1; l<=31; l++) { //倍增2^l,求出路徑長度爲2^l的路徑條數
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)
				for(int k=1; k<=n; k++)
					A[l][i][j]^=A[l-1][i][k]*A[l-1][k][j];

	}
	for(int i=1; i<=q; i++) {
		ll x;
		scanf("%lld",&x);
		printf("%lld\n",work(x));
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章