• 牛客NOIP暑期七天营-TG3 赛后题解


    牛客NOIP暑期七天营-提高组3

    比赛链接

    官方题解

    A-破碎的矩阵

    题解

    标签:推结论+快速幂

    挺妙的一道题??比赛的时候想到了但不敢确定正确性。

    首先很容易想到,对于一个二进制数它的每一位都是独立的。那么下面的结论对于(x=1)(x=2^{30}-1)(2^{60}-1)这三组都同样适用。

    大致思路就是对于一个(n*m)的矩阵,它左上角的子矩阵((n-1)*(m-1))先随便怎么填,剩下的第(n)行,第(m)列根据剩余的异或值来进行调整,那么对于子矩阵中的任意一组填法,剩下的一列一行都有且仅有一组相应的填法。那么最终答案就为(ans=(x+1)^{(n-1)*(m-1)}),底是(x+1)是因为对于子矩阵来说,每个元素的取值范围为([0,x]),共(x+1)个数。

    当然大前提是该组数据有解。如何判呢,只要把每行的值异或起来,把每列的值异或起来,若两者不相等则无解,输出0。

    则总的时间复杂度为(O(case*log(nm)))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m;
    ll x,mod;
    ll ksm(ll r,ll d){
        ll res=1;
        while(d){
            if(d&1)res=res*r%mod;
            r=r*r%mod;d>>=1;
        }
        return res;
    }
    int main(){
        int T;scanf("%d",&T);
        while(T--){
            scanf("%d%d%lld%lld",&n,&m,&x,&mod);
            ll sx=0,sy=0;
            for(int i=1;i<=n;i++){
                ll w;scanf("%lld",&w);
                sx^=w;
            }
            for(int i=1;i<=m;i++){
                ll w;scanf("%lld",&w);
                sy^=w;
            }
            if(sx!=sy){puts("0");continue;}
            ll ans=ksm((x+1)%mod,1ll*(n-1)*(m-1));
            printf("%lld
    ",ans);
        }
    }
    

    B-点与面

    题解

    标签:树状数组裸题

    就是计个数的事,感觉难度比第一题简单很多。。

    维护4个树状数组,然后循环一遍边计数边往上加就完事了,注意顺序应该是从(5->4->3->2->1)不然可能会出现重复计数。

    时间复杂度为(O(NlogN))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define debug(x) cout<<"### "<<x<<endl;
    #define For(a,b,c) for(int a=b;a<=c;++a)
    #define Dor(a,b,c) for(int a=b;a>=c;--a)
    typedef long long ll;
    const int N=1e5+10;
    const ll mod=998244353;
    inline int read(){
        int x=0;char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    int n,num,a[N],b[N];
    ll c[5][N];
    inline ll askA(int x,int id){
    	ll res=0;
    	while(x){
    		res+=c[id][x],res%=mod;
    		x-=x&(-x);	
    	}
    	return res;
    }
    inline ll askB(int x,int id){
    	ll res=0;
    	while(x<=num){
    		res+=c[id][x],res%=mod;
    		x+=x&(-x);
    	}
    	return res;
    }
    inline void addA(int x,ll d,int id){
    	while(x<=num){
    		c[id][x]+=d,c[id][x]%=mod;
    		x+=x&(-x);
    	}
    }
    inline void addB(int x,ll d,int id){
    	while(x){
    		c[id][x]+=d,c[id][x]%=mod;
    		x-=x&(-x);
    	}	
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++)a[i]=read(),b[i]=a[i];	
    	sort(b+1,b+n+1);
    	num=unique(b+1,b+n+1)-b-1;
    	for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+num+1,a[i])-b+1;
    	num++;
    	ll ans=0;
    	for(int i=1;i<=n;i++){
    		//5
    		ll tmp=askA(a[i]-1,4)%mod;
    		ans+=tmp,ans%=mod;
    		//4
    		tmp=askB(a[i]+1,3)%mod;
    		addA(a[i],tmp,4);
    		//3
    		tmp=askA(a[i]-1,2)%mod;
    		addB(a[i],tmp,3);
    		//2
    		tmp=askB(a[i]+1,1)%mod;
    		addA(a[i],tmp,2);
    		//1
    		addB(a[i],1,1);
    	}	
    	printf("%lld
    ",ans%mod);
    }
    

    C-信息传递

    题解

    标签:断环为链+线段树区间维护最值+倍增

    算法一:

    适用于:60%数据,(li,ri<=n<=10^3)

    构图:很容易想到将问题转化为图上的最短路。对于某个人(i),它的传播范围为([li,ri]),我们由(i)向这个区间的所有点连一条边;

    计算:对于每个点(i),我们以其为源点跑一遍Dijkstra求出单源最短路,然后再枚举n个点,找出其中最远的距离(dis_j),这个值就是答案。

    时间复杂度为(O(N^2logN))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=1e5+10;
    bool nc1;
    inline int read(){}
    int id[2*N],n;
    namespace p60{
        bool f[1010][1010];
        int dis[1010],vis[1010];
        vector<int>e[1010];
        struct node{
            int w,now;
            inline bool operator <(const node &x)const{
                return w>x.w;
            }
        };
        priority_queue<node>q;
        void dijkstra(int s){
            for(register int i=1;i<=n;i++)dis[i]=n,vis[i]=0;
            dis[s]=0;
            q.push((node){0,s});
            while(!q.empty()){
                node x=q.top();
                q.pop();
                int u=x.now;
                if(vis[u])continue;
                vis[u]=1;
                for(register int i=0;i<e[u].size();i++){
                    int v=e[u][i];
                    if(dis[v]>dis[u]+1){
                        dis[v]=dis[u]+1;
                        q.push((node){dis[v],v});
                    }
                }
            }
        }
        void solve(){
            for(register int i=1;i<=n;i++){
                int l=read();
                for(register int j=i+n-1;j>=i+n-l;j--)if(!f[id[i]][id[j]]){
                    f[id[i]][id[j]]=1;
                    e[id[i]].push_back(id[j]);
                }
            }
            for(register int i=1;i<=n;i++){
                int r=read();
                for(register int j=i+1;j<=i+r;j++)if(!f[id[i]][id[j]]){
                    f[id[i]][id[j]]=1;
                    e[id[i]].push_back(id[j]);
                }  
            }
            for(register int i=1;i<=n;i++){
                int ans=0;
                dijkstra(i);
                for(register int j=1;j<=n;j++)if(dis[j]>ans)ans=dis[j];
                printf("%d ",ans);
            }
        }
    }
    int li[N],ri[N];
    bool nc2;
    int main(){
        n=read();
        for(int i=1;i<=n;i++)id[i]=id[i+n]=i;
        if(n<=1000){
            p60::solve();
            return 0;
        }
    }
    

    算法二:

    适用于:100%数据,(li,ri<=n<=10^5)

    首先断环为链,开三倍数组。

    我们发现对于每一秒的传递,(i)能传到的最左边的人要么不变要么更左,(i)能传到的最右边的人要么不变要么更右。并且,[最左的人,最右的人]这一段区间里的所有人在此时都已经收到消息了。

    根据这个单调性,我们可以维护两个数组(li[x][i],ri[x][i]),表示在(2^i)秒时,(x)能传递到的最左最右的人,看出用了倍增吧,那么询问时也可以利用倍增跳来快速找出答案。

    接下来的任务就是如何维护呢——官方题解中给出了ST表维护最值,下面的代码中用的是线段树。

    对于树上的节点(o)我们维护两个数组(b[o][t][0/1]),表示在(2^t)秒内节点(o)包括的范围([l,r])中,能跳的最左,能跳的最右。

    接下来一个时间一个时间的转移维护,查询时也是在树上进行的。

    总的时间复杂度为(O(Nlog^2N))

    详见代码8:

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
        int x=0;char c=getchar();
        while(c<'0'||c>'9')c=getchar();
        while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
        return x;
    }
    const int N=3e5+10;
    int n,li[N][18],ri[N][18];
    int b[N<<2][18][2];
    int limL,limR,k,len;
    void build(int o,int l,int r){
        if(l==r){
            b[o][k][0]=li[l][k];
            b[o][k][1]=ri[l][k];
            return;
        }
        int mid=l+r>>1;
        build(o<<1,l,mid),build(o<<1|1,mid+1,r);
        b[o][k][0]=min(b[o<<1][k][0],b[o<<1|1][k][0]);
        b[o][k][1]=max(b[o<<1][k][1],b[o<<1|1][k][1]);
    }
    void query(int o,int l,int r,int ql,int qr){
        if(ql<=l&&r<=qr){
            limL=min(limL,b[o][k][0]);     
            limR=max(limR,b[o][k][1]);
            return;
        }
        int mid=l+r>>1;
        if(ql<=mid)query(o<<1,l,mid,ql,qr);
        if(qr>mid)query(o<<1|1,mid+1,r,ql,qr);
    }
    int main(){
        n=read();len=3*n;
        for(register int i=1;i<=n;i++){
            int x=read();
            for(register int j=i;j<=len;j+=n)li[j][0]=max(1,j-x);
        }
        for(register int i=1;i<=n;i++){
            int x=read();
            for(register int j=i;j<=len;j+=n)ri[j][0]=min(3*n,j+x);
        }
        if(n==1){puts("0");return 0;}
        build(1,1,len);
         
        for(k=1;k<=17;k++){
            for(register int i=1;i<=len;i++){
                limL=1e9,limR=0;
                k--;
                query(1,1,len,li[i][k],ri[i][k]);  
                k++;
                li[i][k]=limL,ri[i][k]=limR;
            }
            build(1,1,len);
        }
     
        for(register int i=n+1;i<=n+n;i++){
            int l=i,r=i,ans=0;
            for(k=17;k>=0;k--){
                limL=1e9,limR=0;
                query(1,1,len,l,r);
                if(limR-limL+1>=n)continue;
                l=limL,r=limR,ans+=1<<k;
            }
            printf("%d ",ans+1);
        }
    }
    
  • 相关阅读:
    SQL Server系统表sysobjects介绍
    tofixed方法 四舍五入
    (function($){})(jQuery);
    DOS批处理命令-字符串操作
    IF ERRORLEVEL 和 IF %ERRORLEVEL% 区别
    Gpupdate命令详解
    DOS批处理中%cd%和%~dp0的区别
    SetACL 使用方法详细参数中文解析
    Lazarus 1.6 增加了新的窗体编辑器——Sparta_DockedFormEditor.ipk
    Lazarus 1.44升级到1.6 UTF8处理发生变化了
  • 原文地址:https://www.cnblogs.com/Tieechal/p/11389669.html
Copyright © 2020-2023  润新知