• 【考试总结】20220321


    Board Game

    \(k\) 个非法状态在棋盘上所有可能的出现位置表示成 \(nm\) 位的二进制数,做高维前缀和来标记非法

    之后是一个平凡的 \(\rm SG\) 函数问题,不另赘述,但是复杂度过高

    注意到 \(vis,\rm dp\) 数组中的元素的值域是 \(\{0,1\}\) 所以考虑将相邻的 \(64\) 个数压成一个 \(64\)unsigned long long,这样子在高维前缀和的时候做一次 \(\rm OR\) 操作就可以实现 \(64\) 个位的转移

    此时仍然需要处理一个数字内部的转移,这里子母集关系只会出现在 \(0\sim 63\) 之间,预处理 \(64\)unsigned long long \(\rm sub_i\) 表示 \(0\sim 63\) 中哪些是 \(i\) 的子集

    这样子每次找到 \(\rm sub_j\& vis_i\) 就可以得到数字内部的转移了

    而后面 \(\rm DP\) 的部分也是类似的,预处理每个 \(0\sim 63\) 添加一个 \(1\) 能走到内部的哪个元素,转移判定为 \(1\) 的元素个数是不是等于集合大小即可

    Code Display
    int n,m,lim,k;
    ull vis[1<<21],dp[1<<21];
    char s[30][30];
    inline void Vis_set(int pos) { vis[pos >> 6] |= 1ull << (pos & 63); }
    inline ull Vis(int pos) { return vis[pos >> 6] >> (pos & 63) & 1; }
    namespace Sub1{
        bool vis[1<<22],dp[1<<22];
        inline int dfs(int x){
            if(vis[x]) return dp[x];
            vis[x]=1;
            int U=(lim-1)^x;
            while(U){
                int lb=U&(-U);
                if(!dfs(x^lb)) return dp[x]=1;
                U-=lb;  
            } return dp[x];
        }
        inline void main(){
            while(k--){
                int h=read(),w=read();
                rep(i,1,h) scanf("%s",s[i]+1);
                for(int dx=0;dx<=n-h;++dx){
                    for(int dy=0;dy<=m-w;++dy){
                        int st=0;
                        for(int i=1;i<=h;++i){
                            for(int j=1;j<=w;++j){
                                if(s[i][j]=='1') st|=1<<((i+dx-1)*m+j+dy-1);
                            }
                        }
                        vis[st]=1;
                    }
                }
            }
            lim=1<<(n*m); 
            for(int p=2;p<=lim;p<<=1){
                int len=p>>1;
                for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l) vis[l+len]|=vis[l];
            }
            puts(dfs(0)?"Alice":"Bob");
            exit(0);
        }
    }
    signed main(){
        freopen("game.in","r",stdin); freopen("game.out","w",stdout);
        n=read(); m=read(); k=read();
        if(n*m<=22) Sub1::main();
        while(k--){
            int h=read(),w=read();
            rep(i,1,h) scanf("%s",s[i]+1);
            for(int dx=0;dx<=n-h;++dx){
                for(int dy=0;dy<=m-w;++dy){
                    int st=0;
                    for(int i=1;i<=h;++i){
                        for(int j=1;j<=w;++j){
                            if(s[i][j]=='1') st|=1<<((i+dx-1)*m+j+dy-1);
                        }
                    }
                    Vis_set(st);
                }
            }
        }
        vector<ull> Sub(64);
        for(int i=0;i<64;++i){
            Sub[i]|=1;
            for(int j=i;j;j=(j-1)&i) Sub[i]|=1ull<<j;
        }
        lim=1<<(n*m-6);
        for(int i=0;i<lim;++i){
            for(int j=0;j<64;++j) if(vis[i]&Sub[j]) vis[i]|=1ull<<j;
        }
        for(int p=2;p<=lim;p<<=1){
            int len=p>>1;
            for(int k=0;k<lim;k+=p) for(int l=k;l<k+len;++l) vis[l+len]|=vis[l];
        }
        for(int i=0;i<64;++i){
            Sub[i]=0;
            rep(j,0,5) if(i>>j&1) Sub[i^(1<<j)]|=1ull<<i;
        }
        vector<int> bit(64);
        rep(i,0,63) bit[i]=__builtin_popcountll(Sub[i]);
        for(int i=lim-1;i>=0;--i){
            for(int j=0;j<n*m-6;++j) if(!(i>>j&1)) dp[i]|=~dp[i^(1<<j)];
            for(int j=63;j>=0;--j) if(!(vis[i]>>j&1)){
                if(dp[i]>>j&1) continue;
                if(__builtin_popcountll(dp[i]&Sub[j])!=bit[j]) dp[i]|=1ull<<j;
            }else if(dp[i]>>j&1) dp[i]^=(1ull<<j);
        }
        puts((dp[0]&1)?"Alice":"Bob");
        return 0;
    }
    
    

    Difference Query

    首先注意给定的序列是排列

    考察 \(f(x,x)\) 做若干步逆操作得到的形式可以发现 \(\exists p,\frac{x}{\rm gcd(x,y)}+\frac{y}{\rm gcd(x,y)}=2^p\Rightarrow f(x,y)=p\),否则 \(f(x,y)=0\)

    扫描线,对于每个 \(i\) 枚举 \(a_i\) 的因子作为 \(\rm gcd\),再枚举 \(2^p\) 作为答案,找到所有带来贡献的数字在线段树上执行区间加法即可

    回答完询问之后再把每个 \(a_i\) 放到其因子的标号为 \(\frac {a_i}d\) 的桶里面即可,空间复杂度是 \(\Theta(n\ln n)\) 的,加一些剪枝可以获得小常数

    Code Display
    const int N=1e5+10;
    int ans[N],n,a[N],Q,Mx[N];
    vector<int> Div[N];
    vector<vector<pair<int,int> > > app[N];
    struct Seg{
        #define ls p<<1
        #define rs p<<1|1
        #define lson p<<1,l,mid
        #define rson p<<1|1,mid+1,r
        int fir[N<<2],len[N<<2],delt[N<<2];
        inline void build(int p,int l,int r){
            len[p]=r-l+1; if(l==r) return ;
            int mid=(l+r)>>1; build(lson); build(rson);
            return ;
        }
        inline void upd(int st,int ed,int fr,int d,int p=1,int l=1,int r=n){
            if(st<=l&&r<=ed) return fir[p]+=(l-st)*d+fr,delt[p]+=d,void();
            int mid=(l+r)>>1;
            if(st<=mid) upd(st,ed,fr,d,lson); if(ed>mid) upd(st,ed,fr,d,rson);
            return ;
        }
        inline int Query(int pos,int p=1,int l=1,int r=n){
            int res=fir[p]+delt[p]*(pos-l); if(l==r) return res;
            int mid=(l+r)>>1;
            if(pos<=mid) return res+Query(pos,lson);
            else return res+Query(pos,rson);
        }
        #undef ls
        #undef rs
        #undef lson
        #undef rson
    }T;
    vector<pair<int,int> >qu[N];
    signed main(){
        freopen("func.in","r",stdin); freopen("func.out","w",stdout);
        n=1e5;
        rep(i,1,n){
            for(int j=i;j<=n;j+=i) Div[j].emplace_back(i);
            app[i].resize(n/i+2);
        }
        n=read();
        rep(i,1,n) a[i]=read();
        Q=read();
        for(int i=1;i<=Q;++i){
            int l=read(),r=read();
            qu[r].emplace_back(l,i);
        }
        for(int i=1;i<=n;++i){
            for(auto d:Div[a[i]]) if((a[i]/d)&1){
                int quo=a[i]/d,p=1;
                while((1<<p)<quo) ++p;
                while(Mx[d]>=(1<<p)-quo){
                    for(auto &rng:app[d][(1<<p)-quo]){
                        int len=rng.sec-rng.fir+1;
                        T.upd(rng.fir,rng.sec,len*p,-p);
                        if(rng.fir!=1) T.upd(1,rng.fir-1,len*p,0);
                    }
                    ++p;
                }
            }
            for(auto q:qu[i]) ans[q.sec]=T.Query(q.fir);
            for(auto d:Div[a[i]]) if((a[i]/d)&1){
                int quo=a[i]/d;
                if(!app[d][quo].size()||app[d][quo].back().sec<i-1) app[d][quo].emplace_back(i,i);
                else app[d][quo].back().sec=i;
                ckmax(Mx[d],quo);
            }
        }
        rep(i,1,Q) print(ans[i]);
        return 0;
    }
    

    Minimal DFA

    一个识别 \(\Sigma \in\{0,1\},L\subset \Sigma^n,L\neq \empty\)\(\rm DFA\) 应当有如下几个性质:

    • 可以将点分成 \(n+1\) 层,否则能识别的长度不唯一

    下设 \(k_i\) 表示 \(\rm DFA\) 的第 \(i\) 层点的数量

    • \(k_i\le k_{i-1}\times |\Sigma|\)

    • \(k_i+1\le (k_i+1)^2\)

      这个是因为第 \(i\) 层的每个点有 \((k_{i+1}+1)^2\) 种后继连接方式,\(+1\) 是不选择任何连边,但是不能两个都不连,也不能出现两个相同,所以得到解释

    第二条从层数小向层数大限制,第三条从层数大到小限制,那么一个高精度类即可解决

    找到第一个 \(k_{i+1}\neq 2k_{i}\)\(i\) 来进行后面两问的计算:

    (下简记从某个第 \(i+1\) 层的点走到终点的路径上 \(|\Sigma_E|\) 的乘积为其走法)

    如果 \(k_{i+1}\ge k_i\) 那么可以证明 \(k_{i+1}=2k_i-1\),将 \(i+1\) 层每个点连接出边之后 \(i\) 层的点只剩下 \(1\) 的度,最大就是再连 \(2^{n-i}\) 的走法的点,最小就是不连

    否则考虑先给所有第 \(i+1\) 层的点连接出边,根据最大化/最小化需求将余下的出边分配终点,以最大化为例

    开始考虑每个 \(i+1\) 层的点分配出边是涉及的 \(i\) 层点的另一个出边都连向 \(2^{n-i}\) 走法的点

    从大到小枚举剩下点连出去的两条边的走法之和 \(v\),这时候使用组合数即可得到方案 \(\binom{2^{n-i+1}}v\),注意要减去已经走过的方案数 \([v\ge 2^{n-i}]\binom{2^{n-i}}{v-2^{n-i}}\)

    最小化同理,一开始强制连出的点不选第二个终点,后面枚举边权从小往大即可

    Code Display
    const int Bas=1e8;
    struct node{
        vector<int> a;
        inline int size(){return a.size();}
        inline void adjust(){
            int siz=a.size();
            for(int i=0;i<siz-1;++i) a[i+1]+=a[i]/Bas,a[i]%=Bas;
            while(a[siz-1]>=Bas){
                a.emplace_back(a[siz-1]/Bas);
                a[siz-1]%=Bas;
                ++siz;
            }
            while(siz>1&&!a[siz-1]) a.pop_back(),--siz;
            return ;
        }
        inline void init(int x){
            a.clear();
            while(x) a.push_back(x%Bas),x/=Bas;
            return ;
        }
        //Attention: const node b is not available here!
        //we Use function size which may change itself though it actually don't
        bool operator <(node b)const{
            if(a.size()!=b.size()) return a.size()<b.size();
            int len=a.size();
            for(int i=len-1;~i;--i) if(a[i]^b.a[i]) return a[i]<b.a[i];
            return 0;
        }
        bool operator ==(node b)const{
            if(b.size()!=a.size()) return 0;
            int len=a.size();
            for(int i=0;i<len;++i) if(a[i]!=b.a[i]) return 0;
            return 1;
        }
        bool operator <=(node &b)const{return *this<b||*this==b;}
        node operator +(node b)const{
            node res;
            int s1=a.size(),s2=b.a.size(); res.a.resize(max(s1,s2));
            for(int i=0;i<s1;++i) res.a[i]+=a[i];
            for(int i=0;i<s2;++i) res.a[i]+=b.a[i];
            res.adjust();
            return res;
        }
        node operator -(node b)const{
            node res=*this;
            int s1=a.size(),s2=b.size();
            for(int i=0;i<s2;++i){
                res.a[i]-=b.a[i];
                if(res.a[i]<0) res.a[i]+=Bas,res.a[i+1]--;
            }
            for(int i=s2;i<s1;++i) if(res.a[i]<0) res.a[i+1]--,res.a[i]+=Bas;
            res.adjust();
            return res;
        }
        node operator *(node b)const{
            node res; res.a.resize(a.size()+b.size()-1);
            for(int i=0;i<a.size();++i) for(int j=0;j<b.size();++j) res.a[i+j]+=a[i]*b.a[j];
            res.adjust();
            return res;
        }
        bool operator !=(node &b){return !(*this==b);}
        node operator *(const int &p)const{node t; t.init(p); return *this*t;}
        node operator +(const int &p)const{node t; t.init(p); return *this+t;}
        node operator -(const int &p)const{node t; t.init(p); return *this-t;}
        inline void output(){
            int siz=a.size(); printf("%lld",a[siz-1]);
            for(int i=siz-2;~i;--i) printf("%08lld",a[i]);
            putchar(' ');
            return ;
        }
    };
    const int N=1010;
    node A[N],pw[1030],C[1030][1030],ans;
    int n,Div;
    inline void solve_min(int Div){
        node sum; 
        node nds=A[Div]-A[Div+1]; Div=n-Div;
        sum=pw[1<<Div]*(1<<(Div-1));
        for(int v=1;v<=(1<<(Div+1));++v){
            node cur=C[1<<(Div+1)][v];
            if(v<=(1<<Div)){
                cur=cur-C[1<<Div][v];
            }
            if(nds<cur) cur=nds;
            sum=sum+(cur*v);
            nds=nds-cur;
            if(nds.size()==1&&nds.a[0]==0) break;
        }
        sum.output();
    }
    inline void solve_max(int Div){
        node sum; 
        node nds=A[Div]-A[Div+1]; Div=n-Div;
        sum=pw[1<<Div]*(1<<(Div-1))+((pw[1<<Div]-1)*(1<<Div));
        for(int v=(1<<(Div+1));v>=1;--v){
            node cur=C[1<<(Div+1)][v];
            if(v>=(1<<Div)){
                cur=cur-C[1<<Div][v-(1<<Div)];
            }
            if(nds<cur) cur=nds;
            sum=sum+(cur*v);
            nds=nds-cur;
            if(nds.size()==1&&nds.a[0]==0) break;
        }
        sum.output();
    }
    signed main(){
        freopen("dfa.in","r",stdin); freopen("dfa.out","w",stdout);
        n=1024;
        C[0][0].init(1); pw[0].init(1);
        for(int i=1;i<=n;++i){
            C[i][0].init(1); pw[i]=pw[i-1]*2;
            for(int j=1;j<=i;++j){
                C[i][j]=C[i-1][j]+C[i-1][j-1];
            }
        }
        n=read();
        if(n==1) puts("2 1 2"),exit(0);
        if(n==2) puts("4 2 3"),exit(0);
        A[1].init(1); A[n+1].init(1);
        for(int i=2;i<=n;++i) A[i]=A[i-1]*2;
        for(int i=n;i>=1;--i){
            node tmp=A[i+1]+1; tmp=tmp*tmp-1;
            if(A[i]<=tmp) break;
            A[i]=tmp;
        }
        for(int i=1;i<=n+1;++i) ans=ans+A[i];
        ans.output();
        for(int i=1;i<=n;++i) if(A[i]*2!=A[i+1]){Div=i; break;}
        if(A[Div]<A[Div+1]){
            (A[Div]*(1<<(n-Div))).output();
            ((A[Div]+1)*(1<<(n-Div))).output();
        }else{
            solve_min(Div);
            solve_max(Div);       
        }
        return 0;
    }
    

  • 相关阅读:
    项目人力资源管理
    以太网交换机
    邮件协议简单学习
    信息系统开发方法
    项目成本管理
    oracle学习笔记002---oracle的体系结构
    007 项目进度管理
    乘法逆元
    RMQ __ST
    中国剩余定理(CRT)
  • 原文地址:https://www.cnblogs.com/yspm/p/16037729.html
Copyright © 2020-2023  润新知