• 2021杭电多校第一场题解


    A

    发现一个数可以作为模数当且仅当该数<=(n-1)/2,然后取(n-1)/2的最高位然后是2^x-1即可

    B

    对于圆这种难处理的东西,我们可以用KD-Tree来维护,这个思维比较好想,但代码难写,先放着有时间填坑

    C

    数据范围是个坑人的玩意!

    很容易知道环上的点度数都是2,而每个环没有重边,说明图中要是均为环,当且仅当每个点的度数都是偶数。而有数字的方格也是一个限制。对于奇偶性的限制,可以考虑解异或方程组,把每条边当成一个未知数即可。解方程可能会TLE,于是可以用bitset优化,时间复杂度O(n^3m^3/w)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e3,mod=998244353;
    int n,m,tot,num,idx[N][N],idy[N][N];
    char s[N];
    bitset<N>a[N];
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            tot=num=0;
            for(int i=1;i<=n;++i)for(int j=1;j<m;++j)idx[i][j]=++tot;
            for(int i=1;i<n;++i)for(int j=1;j<=m;++j)idy[i][j]=++tot;
            ++tot;
            for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
            {
                a[++num].reset();
                if(i>1)a[num][idy[i-1][j]]=1;
                if(j>1)a[num][idx[i][j-1]]=1;
                if(i<n)a[num][idy[i][j]]=1;
                if(j<m)a[num][idx[i][j]]=1;
            }
            for(int i=1;i<n;++i)
            {
                scanf("%s",s+1);
                for(int j=1;j<m;++j)
                if(s[j]!='.')
                {
                    a[++num].reset();
                    a[num][idx[i][j]]=a[num][idx[i+1][j]]=a[num][idy[i][j]]=a[num][idy[i][j+1]]=1;
                    a[num][tot]=s[j]-'0';
                }
            }
            int pos=1;
            for(int j=1;j<tot;++j)
            {
                int x=0;
                for(int i=pos;i<=num;++i)if(a[i][j]){x=i;break;}
                if(!x)continue;
                if(x!=pos)swap(a[pos],a[x]);
                for(int i=1;i<=num;++i)if(i!=pos&&a[i][j])a[i]^=a[pos];
                ++pos;
            }
            int ans=1;
            for(int i=pos;i<=num;++i)if(a[i].any()){ans=0;break;}
            for(int i=pos;i<tot;++i)ans=ans*2%mod;
            printf("%d
    ",ans);
        }
    }
    View Code

    E

    如果x为质数向2连边,否则向其中一个约数连边。质数可以用线性筛处理,然后用前缀和统计。时间复杂度O(n),代码略。

    F

    类似十二省联考异或粽子的写法,建立字典树,每个节点表示最后出现的位置,然后建立字典树前查询即可。

    时间复杂度O(30n)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+7;
    int n,k,cnt,l,r,ch[N*30][2],pre[N*30];
    void build(int x,int id)
    {
        int u=1,op;
        pre[u]=id;
        for(int i=29;~i;--i)
        {
            op=(x>>i)&1;
            if(!ch[u][op])ch[u][op]=++cnt;
            u=ch[u][op];
            pre[u]=id;
        }
    }
    void find(int x,int id)
    {
        int u=1,mx=-1,op,pos;
        for(int i=29;~i;--i)
        {
            op=(x>>i)&1,pos=(k>>i)&1;
            if(!pos)mx=max(mx,pre[ch[u][op^1]]);
            u=ch[u][op^pos];
            if(!u)break;
        }
        mx=max(mx,pre[u]);
        if(mx!=-1&&id-mx<r-l)l=mx,r=id;
    }
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&k);
            pre[0]=-1;
            for(int i=1;i<=n*30+1;++i)ch[i][0]=ch[i][1]=0,pre[i]=-1;
            l=0,r=n+1,cnt=1;
            build(0,0);
            int now=0;
            for(int i=1,x;i<=n;++i)
            {
                scanf("%d",&x),now^=x;
                find(now,i);
                if(i<n)build(now,i);
            }
            if(r==n+1)puts("-1");else printf("%d %d
    ",l+1,r);
        }
    }
    View Code

    G

    f[i]=(n-2)f[i-1]+(n-1)f[i-2],推式子可以得到f[t]=((n-1)^t+(n-1)*(-1)^t)/n

    然后分奇偶性讨论可以变成(n-1)^(2a)=b,使用BSGS即可解决

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll mod=998244353;
    ll n,x,B;
    unordered_map<ll,ll>mp;
    ll qpow(ll a,ll b)
    {
        ll ret=1;
        while(b)
        {
            if(b&1)ret=ret*a%mod;
            a=a*a%mod,b>>=1;
        }
        return ret;
    }
    int main()
    {
        int T;scanf("%d",&T);
        B=sqrt(mod);
        while(T--)
        {
            scanf("%lld%lld",&n,&x);
            x=x*n%mod*qpow(n-1,mod-2)%mod;
            mp.clear();
            mp[1]=0;
            ll f1=(x+mod-1)*(n-1)%mod,f2=(x+1)%mod,base=(n-1)*(n-1)%mod;
            if(f1==1){puts("0");continue;}
            if(f2==1){puts("1");continue;}
            ll p=1,ans=-1;
            for(int i=1;i<=B;i++)
            {
                p=p*base%mod;
                mp[p]=i;
                if(f1==p){ans=2*i;break;}
                if(f2==p){ans=2*i+1;break;}
            }
            if(ans!=-1){printf("%lld
    ",ans);continue;}
            p=qpow(p,mod-2);
            for(ll k=1,pw=p;k*B<mod;k++,pw=pw*p%mod)
            {
                if(mp.count(f1*pw%mod)){ans=2ll*(k*B+mp[f1*pw%mod]);break;}
                if(mp.count(f2*pw%mod)){ans=2ll*(k*B+mp[f2*pw%mod])+1;break;}
            }
            printf("%lld
    ",ans);
        }
    }
    View Code

    H

    转化为0,1矩阵,每个位置1代表比上面小,然后用悬线法求最大01矩阵即可

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5005;
    int n,m,ans,tot,a[N][N],b[N][N],h[N],q[N];
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
                b[i][j]=(i==1)?0:(a[i][j]>=a[i-1][j]);
            }
            for(int i=1;i<=m;i++)h[i]=0;
            ans=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=1;j<=m;j++)if(!b[i][j])h[j]=1;else h[j]++;
                tot=h[m+1]=0;
                for(int j=1;j<=m+1;j++)
                {
                    while(tot&&h[q[tot]]>h[j])
                    {
                        ans=max(ans,(j-q[tot-1]-1)*h[q[tot]]);
                        --tot;
                    }
                    q[++tot]=j;
                }
            }
            printf("%d
    ",ans);
        }
    }
    View Code

    I

    签到题,按照kruskal的加边方式,从小到大加边,注意考虑边权相同的情况即可

    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+7,M=5e5+7;
    struct edge{int x,y,z;}e[M];
    int n,m,k,fa[N];
    bool cmp(edge a,edge b){return a.z<b.z;}
    int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
    int main()
    {
        int T;scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&n,&m,&k);
            for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
            for(int i=1;i<=n;i++)fa[i]=i;
            sort(e+1,e+m+1,cmp);
            int num=n,D=1e9+7;
            bool flag=1;
            if(k==n)D=0;
            for(int i=1;i<=m&&D;i++)
            {
                int x=e[i].x,y=e[i].y,z=e[i].z;
                x=find(x),y=find(y);
                if(x!=y)
                {
                    if(num==k+1)D=z;
                    else if(num<=k)
                    {
                        if(z<=D)flag=0;
                        break;
                    }
                    fa[x]=y,--num;
                }
            }
            if(!flag||D==1e9+7)puts("-1");else printf("%d
    ",D);
        }
    }
    View Code

    J

    询问是1e5次,而且没有修改,所以是裸的莫队,代码略

    持续更新中……

  • 相关阅读:
    贝尔级数
    NOIP2018 退役记
    Codeforces1106F 【BSGS】【矩阵快速幂】【exgcd】
    codeforces1111 简单题【DE】简要题解
    BZOJ4836: [Lydsy1704月赛]二元运算【分治FFT】【卡常(没卡过)】
    BZOJ3771: Triple【生成函数】
    Codeforces 1096G. Lucky Tickets【生成函数】
    Codeforces1099F. Cookies【DP】【线段树】【贪心】【博弈】【沙比提(这是啥算法)】
    Codeforces gym101955 A【树形dp】
    BZOJ3551: [ONTAK2010]Peaks加强版【Kruskal重构树】【主席树】
  • 原文地址:https://www.cnblogs.com/hfctf0210/p/15068772.html
Copyright © 2020-2023  润新知