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;
}