[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;
}
posted @ 2020-05-28 21:52  Arextre  阅读(175)  评论(0编辑  收藏  举报