zoukankan      html  css  js  c++  java
  • 牛客OI月赛12-提高组题解

    牛客OI月赛12-提高组

    当天晚上被(loli)要求去打了某高端oj部分原创的模拟赛,第二天看了牛客的题觉得非常清真,于是就去写了

    不难发现现场写出(260 ext{pts})并不需要动脑子,而且(260 ext{pts})甚至还有(rk2),感觉没打非常吃亏

    A.小w的进制转换

    大概理解一下就是询问(1)(n)里有多少个数的二进制表示是反回文形式的,即对称位置是相反的,比如(1001,101010)

    之后根据题意,这个反回文的二进制串长度必须是偶数(因为长度为奇数的串对称中心和对称中心不相反)

    也不难发现这个反回文串确定了前一半后面就确定了

    考虑枚举这个反回文的串的长度,显然长度小于(n)的长度的串,我们只需要让最高填(1),最低位填(0),之后除去最高位的前(frac{len-2}{2})随便填即可

    对于长度等于(n)的串,还是最高填(1),最低位填(0),除去最高位的前(frac{len-2}{2})位填一个严格小于(n)的前(frac{len-2}{2})位的数,这样就能保证严格小于(n)

    之后再特判一下前(frac{len-2}{2})位和(n)的前(frac{len-2}{2})位相等时的时候,反对称过去的数是否超过(n)

    代码好像写得有点丑了

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    LL b[65];
    LL n,ans;
    inline void calc(int bit) {
        if(bit&1) return;
        ans+=(1ll<<((bit-2)/2));
    }
    int a[64];int tot;
    inline int solve() {
        b[0]=1;int W=2*tot+1;
        for(re int i=0;i<=tot;++i) b[W-i]=b[i]^1;
        LL m=0;
        for(re int i=0;i<=W;++i)
            m<<=1ll,m|=b[i];
        return m<=n;
    }
    inline void Calc() {
        scanf("%lld",&n);;
        int now=0;tot=0;ans=0;
        for(re LL i=63;i>=0;--i) {
            if(!now) {
                if(n>>i&1) {
                    now=1;
                    if((i-1)&1) continue;
                    for(re LL j=i-1;j>=0;--j) {
                        if(tot*2>=i-1) continue;
                        b[++tot]=n>>j&1;
                    }
                    LL k=0;
                    for(re LL j=1;j<=tot;++j)
                        k|=b[j],k<<=1ll;
                    k>>=1ll;ans=k;
                    if(!tot) ++ans;else ans+=solve();
                }
                continue;
            }
            if(i>0) calc(i+1);
        }
        std::cout<<ans;puts("");
    }
    int main() {
        int T;scanf("%d",&T);
        while(T--) Calc();
        return 0;
    }
    

    B.小doge的快乐阳光跑

    题意:给一张图,求一个权值和路径最小的移动序列,使得移动序列包含两个给定的子序列。

    发现点数只有(10^3),边数也只有(10^4),图相当稀疏,所以完全可以跑(n)遍单源最短路,求出所有点对的之间的最短路

    之后搞一个(dp)就完事了,设(dp_{0/1,i,j})表示当前在第(1/2)个子序列的第(i)个位置,另一个子序列已经经过了前(j)个位置

    转移显然,就是枚举一下下一个点去哪里

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #define mp std::make_pair
    #define max(a,b) ((a)>(b)?(a):(b))
    #define min(a,b) ((a)<(b)?(a):(b))
    inline int read() {
        char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
        while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    typedef std::pair<int,int> pii;
    const int maxn=1e3+5;
    struct E{int v,nxt,w;}e[maxn*20];
    int d[maxn][maxn];
    int n,m,num,head[maxn],vis[maxn],a[maxn],b[maxn],A,B;
    inline void add(int x,int y,int w) {
        e[++num].v=y;e[num].nxt=head[x];head[x]=num;e[num].w=w;
    }
    LL dp[2][105][105];
    std::priority_queue<pii,std::vector<pii>,std::greater<pii> > q;
    inline void Dij(int s) {
        for(re int i=1;i<=n;++i) d[s][i]=1e9,vis[i]=0;
        d[s][s]=0,q.push(mp(d[s][s],s));
        while(!q.empty()) {
            int k=q.top().second;q.pop();
            if(vis[k]) continue;vis[k]=1;
            for(re int i=head[k];i;i=e[i].nxt)
            if(d[s][e[i].v]>d[s][k]+e[i].w) {
                d[s][e[i].v]=d[s][k]+e[i].w;
                q.push(mp(d[s][e[i].v],e[i].v));
            }
        }
    }
    int main() {
        n=read(),m=read();
        for(re int x,y,z,i=1;i<=m;i++)
            x=read(),y=read(),z=read(),add(x,y,z),add(y,x,z);
        for(re int i=1;i<=n;i++) Dij(i);
        A=read();for(re int i=1;i<=A;i++) a[i]=read();
        B=read();for(re int i=1;i<=B;++i) b[i]=read();
        memset(dp,20,sizeof(dp));
        dp[0][1][0]=0,dp[1][0][1]=0;
        for(re int t=1;t<A+B;++t)
            for(re int j=0;j<=t&&j<=A;++j) {
                re int k=t-j;
                if(k>B) continue;
                if(j<A) dp[0][j+1][k]=min(dp[0][j+1][k],dp[0][j][k]+d[a[j]][a[j+1]]),
                    dp[0][j+1][k]=min(dp[0][j+1][k],dp[1][j][k]+d[b[k]][a[j+1]]);
                if(k<B) dp[1][j][k+1]=min(dp[1][j][k+1],dp[0][j][k]+d[a[j]][b[k+1]]),
                dp[1][j][k+1]=min(dp[1][j][k+1],dp[1][j][k]+d[b[k]][b[k+1]]);
            }
        printf("%lld
    ",min(dp[1][A][B],dp[0][A][B]));
        return 0;
    }
    

    C.区间异或和异或区间最大值异或区间最小值

    题意就是在给定的一个序列里找到一个区间,让上面那个东西最大

    (60 m pts)是送的,写几个( m subtask)拼一拼就有了

    考虑正解,考虑率大力分治一波,对于区间([l,r])我们考虑一下如何合并([l,mid])([mid+1,r])

    我们把左区间所有后缀以及右区间所有前缀的异或和、最大值、最小值都扫出来,我们提前把最大值和最小值都给异或进去

    考虑合并掉两个区间之后,两个最大值中较小的那一个就不是最大值了,我们需要把它异或回来;两个最小值中较大的也不是最小值了,也需要把它异或回来

    于是我们考虑从右区间里拿出一个数来,作为最大值较小的去和左区间匹配

    显然可以排序之后利用单调性开一个指针,把左区间比这个最大值大的都扫进来,放到一个数据结构里,显然这种数据结构是(trie)

    之后按照最小值讨论一波

    • 左区间的最小值作为最小值,那么我们需要把右区间的最小值给异或回来,去查最小值小于右区间最小值中,异或上这个数最大的

    • 右区间的最小值作为最小值,那么需要把左区间的最小值给异或回来,我们可以对左区间维护一个把最小值异或回来的(trie),在这个(trie)里查最小值大于右区间最小值中,异或上这个数最大的

    我们发现按照最大值把这些数加入(trie)的过程中最小值也是单调的,于是把(trie)可持久化一下就可以查区间异或最大值了

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    #pragma GCC optimize(3)
    #pragma GCC optimize("-fcse-skip-blocks")
    inline int read() {
    	char c=getchar();int x=0;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 maxn=1e5+5;
    const int Bit=30;
    struct Seg {int mn,mx,x;}b[2][maxn];
    int n,a[maxn],c[maxn],cnt,tot,ans;
    int rt[2][maxn],son[maxn*32][2],d[maxn*32];
    inline int max(int a,int b) {return a>b?a:b;}
    inline int min(int a,int b) {return a<b?a:b;}
    int ins(int pre,int x,int w) {
    	int now=++cnt;
    	d[now]=d[pre]+1;
    	if(w==-1) return now;
    	son[now][0]=son[pre][0],son[now][1]=son[pre][1];
    	son[now][x>>w&1]=ins(son[pre][x>>w&1],x,w-1);
    	return now;
    }
    int query(int p1,int p2,int x,int w) {
    	if(w==-1) return 0;
    	int t=x>>w&1;
    	int now=d[son[p2][t^1]]-d[son[p1][t^1]];
    	if(now) return (1<<w)+query(son[p1][t^1],son[p2][t^1],x,w-1);
    	return query(son[p1][t],son[p2][t],x,w-1); 
    }
    inline int find(int x) {
    	int l=1,r=tot,now=0;
    	while(l<=r) {
    		int mid=l+r>>1;
    		if(c[mid]<=x) l=mid+1,now=mid;
    		else r=mid-1;
    	}
    	return now;
    }
    void solve(int l,int r) {
    	if(l==r) return;
    	int mid=l+r>>1;
    	solve(l,mid),solve(mid+1,r);
    	int lenl=mid-l+1,lenr=r-mid;
    	for(re int i=mid+1;i<=r;++i) {
    		b[0][i-mid].mn=min(a[i],b[0][i-1-mid].mn);
    		b[0][i-mid].mx=max(a[i],b[0][i-1-mid].mx);
    		b[0][i-mid].x=a[i]^b[0][i-mid-1].x;
    	}
    	for(re int i=mid;i>=l;--i) {
    		b[1][mid+1-i].mn=min(a[i],b[1][mid-i].mn);
    		b[1][mid+1-i].mx=max(a[i],b[1][mid-i].mx);
    		b[1][mid+1-i].x=a[i]^b[1][mid-i].x;
    	}
    	for(re int i=1;i<=lenl;++i) b[1][i].x^=(b[1][i].mn^b[1][i].mx);
    	for(re int i=1;i<=lenr;++i) b[0][i].x^=(b[0][i].mn^b[0][i].mx);
    	cnt=0,tot=0;int now=lenl;
    	for(re int i=lenr;i;--i) {
    		while(now&&b[1][now].mx>=b[0][i].mx) {
    			++tot;
    			rt[0][tot]=ins(rt[0][tot-1],b[1][now].x,Bit);
    			rt[1][tot]=ins(rt[1][tot-1],b[1][now].x^b[1][now].mn,Bit);
    			c[tot]=b[1][now--].mn;
    		}
    		if(tot) {
    			int x=find(b[0][i].mn);
    			ans=max(ans,query(rt[0][0],rt[0][x],b[0][i].x^b[0][i].mx^b[0][i].mn,Bit));
    			ans=max(ans,query(rt[1][x],rt[1][tot],b[0][i].x^b[0][i].mx,Bit));
    		}
    	}
    	for(re int i=1;i<=tot;i++) rt[0][i]=rt[1][i]=0;
    	cnt=0;tot=0;now=lenr;
    	for(re int i=lenl;i;--i) {
    		while(now&&b[0][now].mx>=b[1][i].mx) {
    			++tot;
    			rt[0][tot]=ins(rt[0][tot-1],b[0][now].x,Bit);
    			rt[1][tot]=ins(rt[1][tot-1],b[0][now].x^b[0][now].mn,Bit);
    			c[tot]=b[0][now--].mn;
    		}
    		if(tot) {
    			int x=find(b[1][i].mn);
    			ans=max(ans,query(rt[0][0],rt[0][x],b[1][i].x^b[1][i].mx^b[1][i].mn,Bit));
    			ans=max(ans,query(rt[1][x],rt[1][tot],b[1][i].x^b[1][i].mx,Bit));
    		}
    	}
    	for(re int i=1;i<=tot;i++) rt[0][i]=rt[1][i]=0;
    }
    int main() {
    	n=read();
    	for(re int i=1;i<=n;i++) a[i]=read(),ans=max(ans,a[i]);
    	b[0][0].mn=b[1][0].mn=1e9;
    	solve(1,n);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    [LeetCode] Baseball Game
    [Linux] Shell Scripts
    [Linux] 正则表达式与文件格式化处理
    [Linux] 学习bash
    [Linux] vim程序编辑器
    [Linux] 文件与文件系统的压缩打包与备份
    [LeetCode] Reverse Words in a String
    [LeetCode] Reverse Integer
    [国嵌笔记][017][Makefile工程管理]
    [国嵌笔记][016][交叉工具链]
  • 原文地址:https://www.cnblogs.com/asuldb/p/11605865.html
Copyright © 2011-2022 走看看