[NOI Online #3]魔法值
CCF的机子跑得是真的慢
题目
题解
\(40\%\) 数据点
对于所有的数据范围,\(1\le n,q\le 100,1\le a_i\le 2^{32}\),我们应该十分敏感,即 \(n\) 很小,但是询问很大,而本题又与图论有关,可以建立邻接矩阵,那么我们应该有个第一判断——此题可用矩阵加速。
十分显然的做法,建立邻接矩阵之后直接矩阵快速幂。
时间复杂度为显然的 \(\mathcal O(qn^3log_a)\),但是如果有比较厉害的大佬,可以发现此题可以使用 bitset
优化,常数上会乘上一个 \(\frac{1}{32}\) ,最后会发现时间复杂度刚好为 \(\mathcal O(qn^3)\)(因为 \(\max\{log_a\}=32\)),刚刚好 \(\mathcal O(1e8)\) ,然后我以为这样对于 \(1s\) 的时限可以过了,然后就没有做更多优化,结果。。。爆掉 \(60pts\) 啊!
还有一个比较小的优化,将 \(a_i\) 排序之后。时间复杂度变为 \(\mathcal O(qn^3log_{\max\{a_i\}})\)
贴一发考试的时候写的代码(带 bitset
优化)
#include<cstdio>
#include<bitset>
#include<algorithm>
#include<cstring>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
// #define int long long
#define int unsigned
// #define int unsigned long long
#define cg (c=getchar())
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T qread(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
#undef cg
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXN=100;
/*
struct matrix{
int a[MAXN+5][MAXN+5],n,m;
matrix(const int N=0,const int M=0):n(N),m(M){memset(a,0,sizeof a);}
matrix clear(){return *this=matrix();}
matrix operator*(const matrix rhs)const{
matrix ret(n,rhs.m);
rep(i,1,n)rep(k,1,rhs.m)rep(j,1,m)
ret.a[i][j]+=a[i][k]*rhs.a[k][j];
return ret;
}
};
*/
struct matrix{
bitset<MAXN+5>a[MAXN+5],b[MAXN+5];
int n,m;
matrix(const int N=0,const int M=0):n(N),m(M){}
matrix operator *(const matrix rhs)const{
matrix ret;
ret.n=n,ret.m=rhs.m;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
ret.a[i][j]=ret.b[j][i]=(a[i]&rhs.b[j]).count()&1;
}
return ret;
}
};
matrix qkpow(matrix a,int n){
matrix ret(a.n,a.n);
rep(i,1,ret.n)ret.a[i][i]=ret.b[i][i]=1;
for(;n>0;n>>=1){
if(n&1)ret=ret*a;
a=a*a;
}return ret;
}
matrix o,acc;
int f[MAXN+5],n,m,q;
inline void Init(){
n=qread(1),m=qread(1),q=qread(1);
acc.n=acc.m=n;
rep(i,1,n)f[i]=qread(1);
int u,v;
rep(i,1,m){
u=qread(1),v=qread(1);
acc.a[u][v]=acc.a[v][u]=acc.b[u][v]=acc.b[v][u]=1;
}
}
int t[MAXN+5],a[MAXN+5],ans[MAXN+5];
inline bool cmp(const int i,const int j){
return a[i]<a[j];
}
inline void Getquery(){
rep(i,1,q)a[i]=qread(1),t[i]=i;
sort(t+1,t+q+1,cmp);
}
inline void solve(){
o.n=1,o.m=n,o.a[1][1]=o.b[1][1]=1;
int calc;
for(int m=1;m<=q;++m){calc=0;
o=o*qkpow(acc,a[t[m]]-a[t[m-1]]);
rep(i,1,n)if(o.a[1][i])
calc^=f[i];
ans[t[m]]=calc;
}
}
signed main(){
// freopen("magic.in","r",stdin);
// freopen("magic.out","w",stdout);
Init();
Getquery();
solve();
rep(i,1,q)writc(ans[i],'\n');
return 0;
}
/*
testData-1:
7 7 10
1 4 3 3 2 2 3
1 2
2 3
1 4
1 6
1 5
6 7
5 7
1
2
3
4
5
6
7
8
9
10
*/
\(100\%\) 数据点
我们发现,其实我们的方法已经十分接近正解了(因为时间复杂度)。
唯一的瓶颈在哪里?我们重复求了加速矩阵的 \(k\) 次方很多次。
而我们要将这个复杂度降下来,只需要将加速矩阵的 \(1,2,4,8....\) 次方全部求出来,最后将 \(a_i\) 二进制拆分之后,乘到初始矩阵上即可。
因为我们的乘法始终都是初始矩阵(一个 \(1\times n\) 的矩阵)乘上一个加速矩阵的某次方(一个 \(n\times n\) 的矩阵),时间复杂度只有 \(\mathcal O(n^2)\),再加上在二进制拆分下,时间复杂度最终只有 \(\mathcal O(qn^2\log_{a_i})\),降掉了一个 \(n\) 。
矩阵乘法依然可以使用 bitset
优化。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i<=i##_end_;++i)
#define fep(i,__l,__r) for(signed i=(__l),i##_end_=(__r);i>=i##_end_;--i)
#define writc(a,b) fwrit(a),putchar(b)
#define mp(a,b) make_pair(a,b)
#define ft first
#define sd second
#define LL long long
#define ull unsigned long long
#define uint unsigned int
#define pii pair< int,int >
#define Endl putchar('\n')
// #define int long long
#define int unsigned
// #define int unsigned long long
#ifdef _GLIBCXX_CSTDIO
#define cg (c=getchar())
template<class T>inline void qread(T& x){
char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
if(f)x=-x;
}
template<class T>inline T qread(const T sample){
T x=0;char c;bool f=0;
while(cg<'0'||'9'<c)f|=(c=='-');
for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48));
return f?-x:x;
}
#undef cg
template<class T>void fwrit(const T x){//just short,int and long long
if(x<0)return (void)(putchar('-'),fwrit(-x));
if(x>9)fwrit(x/10);
putchar(x%10^48);
}
#endif
// template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;}
inline void getInv(int inv[],const int lim,const int MOD){
inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod
return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod;
}
const int MAXN=100;
const int logMAXA=32;
int n,m,q;
struct matrix{
bitset<MAXN+5>a[MAXN+5],b[MAXN+5];
int n,m;
matrix(const int N=0,const int M=0):n(N),m(M){
rep(i,1,n)a[i]=0,b[i]=0;
}
matrix operator *(const matrix rhs)const{
matrix ret(n,rhs.m);
rep(i,1,n)rep(j,1,m){
ret.a[i][j]=ret.b[j][i]=(a[i]&rhs.b[j]).count()&1;
}return ret;
}
matrix operator *=(const matrix rhs){
return (*this)=(*this)*rhs;
}
void clear(){
rep(i,1,n)a[i]=0,b[i]=0;
}
}beg,acc;
inline matrix makeO(){
matrix ret;
rep(i,1,n)ret.a[i][i]=ret.b[i][i]=1;
return ret;
}
matrix a[logMAXA+5];
int f[MAXN+5];
signed main(){
ios::sync_with_stdio(false);
n=qread(1),m=qread(1),q=qread(1);
acc=matrix(n,n);
rep(i,1,n)f[i]=qread(1);
int u,v;
rep(i,1,m){
u=qread(1),v=qread(1);
acc.a[u][v]=acc.a[v][u]=acc.b[u][v]=acc.b[v][u]=1;
}
a[1]=acc;
rep(i,2,logMAXA)a[i]=a[i-1]*a[i-1];
int require,now,ans;
rep(i,1,q){
beg=matrix(1,n);
beg.a[1][1]=beg.b[1][1]=1;
require=qread(1),now=1;
while(require>0){
if(require&1)beg*=a[now];
require>>=1,++now;
}ans=0;
rep(j,1,n)if(beg.a[1][j])
ans^=f[j];
writc(ans,'\n');
}
return 0;
}