Manthan Codefest 19 C(构造) D(线段树)

https://codeforc.es/contest/1208

C:给n(n%4==0) ,用[0,n^2-1]的数,每个数只能用一次,构造n*n矩阵,使得所有行和列的异或值相同。

思路:使得所有每一行和每一列,异或值都是0。连续的4个数,异或起来是0,根据这个构造。

划分成n^2/16个矩阵,类似如下构造:

/*   (o O o)
 *   Author : Rshs
 *   Data : 2019-08-26-16.26
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
int mp[1005][1005];
int main(){
    int n;cin>>n;int cnt=0;
    for(int i=1;i<=n;i+=4){
        for(int j=1;j<=n;j+=4){
            int l=cnt*16;
            for(int x=i;x<=i+3;x++){
                for(int y=j;y<=j+3;y++){
                    mp[x][y]=l++;
                }
            }
            cnt++;
        }
    }
    for(int i=1;i<=n;i++){for(int j=1;j<=n;j++) cout<<mp[i][j]<<' ';puts("");}
    return 0;
}

D:有一未知序列b,给序列a,长度为n,ai=Σ(bj)(其中j<i&&bj<bi),求b。

思路1:线段树维护a的最小值,从确定1的位置一直到确定n的位置(每次都找最后一个为0的位置,且一定找的到)。确定了x的位置后,将[x的位置+1,n]的一段减去x。将x的位置的值 标为无穷大。

/*   (o O o)
 *   Author : Rshs
 *   Data : 2019-08-26-16.40
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
LL a[MX],lz[MX],T[MX];
 
inline void pushUp(int rt){
    T[rt]=min(T[rt<<1],T[rt<<1|1]);
}
 
void pushDown(int rt){
    if(lz[rt]){
        lz[rt<<1]+=lz[rt];
        lz[rt<<1|1]+=lz[rt];
        T[rt<<1]+=lz[rt];
        T[rt<<1|1]+=lz[rt];
        lz[rt]=0;
    }
}
 
void update(int L,int R,LL c,int l,int r,int rt){
    if(l>=L&&r<=R){
        lz[rt]+=c;
        T[rt]+=c;
        return;
    }
    pushDown(rt);
    int m=(l+r)>>1;
    if(m>=L) update(L,R,c,l,m,rt<<1);
    if(m<R)  update(L,R,c,m+1,r,rt<<1|1);
    pushUp(rt);
}
 
int query(int l,int r,int rt){
    if(l==r)
        return l;
    pushDown(rt);
    int m=(l+r)>>1;
    if(T[rt<<1|1]==0) return query(m+1,r,rt<<1|1);
    return query(l,m,rt<<1);
}
int ans[MX];
int main(){
    int n;cin>>n;for(int i=1;i<=n;i++)
    {
        LL sc;scanf("%I64d",&sc);update(i,i,sc,1,n,1);
    }
    for(int i=1;i<=n;i++){
        int id=query(1,n,1);
        ans[id]=i;
        if(id<n)update(id+1,n,(LL)(-i),1,n,1);
        update(id,id,LLONG_MAX/10,1,n,1);
    }
    for(int i=1;i<=n;i++)cout<<ans[i]<<' ';
    return 0;
}

思路2:树状数组,维护序列{1,2,3,4...n},从n到1遍历ai,每次确定bi,二分,尽量大的取树状数组维护的前缀使得小于ai。

然后每次不停的删除已经确定的(因为对后面数据没有影响)。

树状数组常数小,n小,二分的区间较小,跑的还挺快的。

/*   (o O o)
 *   Author : Rshs
 *   Data : 2019-08-26-17.27
 */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LL mod = 1e9+7;
const int MX = 1e6+5;
LL a[MX];LL bit[MX];
void add(LL c,int i,int nn){
    while(i<=nn){
        bit[i]+=c;
        i=i+(i&-i);
    }
}
LL query(int i){//小于a[i]的个数和前缀和
    LL re=0;
    while(i>0){
        re+=bit[i];
        i=i-(i&-i);
    }
    return re;
}
int ans[MX];
int main(){
    int n;cin>>n;for(int i=1;i<=n;i++)scanf("%I64d",&a[i]),add(i,i,n);
    for(int i=n;i>=1;i--){
        int l=0,r=n-1,aa;
        while(l<=r){
            int mid=(l+r)/2;
            if(query(mid)<=a[i])aa=mid,l=mid+1;
            else r=mid-1;
        }
        ans[i]=aa+1;
        add(-aa-1,aa+1,n);
    }
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值