[2019.8.25]codeforces1208 Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2)

概况

排名:304/6994

过题数:5

Rating:\(\color{green}{+44}\)(\(\color{orange}{2147}\))

题目

A. XORinacci

AC时间:5min,490分

题解:

首先按二进制位考虑。

对于每一位,如果\(F(0),F(1)\)为0,那么这一位必然是0;

其余三种都是四位循环的。直接判断即可。

code:

#include<bits/stdc++.h>
using namespace std;
int T,a,b,n,ans;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&a,&b,&n),ans=0;
        for(int i=1;i<=a||i<=b;i<<=1){
            if((a&i)&&(b&i))ans|=i*(n%3!=2);
            else if(a&i)ans|=i*(n%3!=1);
            else if(b&i)ans|=i*(n%3!=0);
        }
        printf("%d\n",ans);
    }
    return 0;
}

B.Uniqueness

AC时间:14min,944分

题解:

离散化,然后枚举起点,枚举终点。对于同一起点,终点可以直接转移。

code:


#include<bits/stdc++.h>
using namespace std;
struct ST{
    int v,id;
}s[2010];
int n,a[2010],tmp,num[2010],t[2010],ans=1e9,tg=1,tt,mn;
bool cmp(ST x,ST y){
    return x.v<y.v;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&s[i].v),s[i].id=i;
    sort(s+1,s+n+1,cmp);
    for(int i=1;i<=n;++i)tmp+=s[i].v!=s[i-1].v,a[s[i].id]=tmp,++num[tmp],tg&=(num[tmp]==1);
    if(tg)return puts("0"),0;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=tmp;++j)t[j]=num[j],tt+=t[j]>1;
        for(mn=i;mn<=n&&tt;++mn)--t[a[mn]],tt-=t[a[mn]]==1;
        if(!tt)ans=min(ans,mn-i);
    }
    printf("%d",ans);
    return 0;
}

C.Magic Grid

AC时间:52min,1188分

(坑死我了)

题解:

两个重要性质:

对于任意非负整数\(k\),有\((4k)xor(4k+1)xor(4k+2)xor(4k+3)=0\)

另外,对于非负整数\(k\)\(0\le p<4\),有\((16k+p)xor(16k+4+p)xor(16k+8+p)xor(16k+12p)=0\)

注意到\(0\ xor\ 1\ xor\ 2\ xor\ 3=0\),上述两点都容易证明。

于是,对于任意一个\(k\),下面方阵的任意行列异或和为0:
\[ \begin{equation} \begin{array}{cccc} 16k&16k+1&16k+2&16k+3\\ 16k+4&16k+5&16k+6&16k+7\\ 16k+8&16k+9&16k+10&16k+11\\ 16k+12&16k+13&16k+14&16k+15 \end{array} \end{equation} \]
由于\(n\)是4的倍数,直接这样构造即可。

code:


#include<bits/stdc++.h>
using namespace std;
int n,mp[1010][1010],tt;
void Work(int x,int y){
    for(int i=0;i<4;++i)for(int j=0;j<4;++j)mp[x+i][y+j]=tt++;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i+=4)for(int j=1;j<=n;j+=4)Work(i,j);
    for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)printf("%d%c",mp[i][j]," \n"[j==n]);
    return 0;
}

D.Restore Permutation

AC时间:1h8min,1456分

(没错我D用了16min,C用了38min)

题解:

考虑从后往前计算。

记在算完一段后缀后,如果在下一个位置放置\(x\),那么所有满足条件的数的和为\(sum_x\)

一开始,\(sum_x=\frac{x(x-1)}{2}\)

对于当前位置\(i\),我们可以二分求得满足\(sum_x=s_i\)\(x\)

然后,我们要更新\(sum\)。相当于对所有\(k>x\),令\(sum_k=sum_k-x\)

线段树维护即可。

还有一个问题,有些数已经放过了,但是我们二分的时候还是会二分到它,也就是说满足\(sum_x=s_i\)的可能是一段\(x\),而不是一个\(x\)

事实上,我们要找的\(x\)应该是所有满足\(sum_x=s_i\)\(x\)中最大的。

code:

#include<bits/stdc++.h>
#define ci const int&
#define Upd(x) (t[x].sum=t[x<<1].sum+t[x<<1|1].sum)
#define Sum(x) (1ll*(x+1)*(x)/2ll)
using namespace std;
struct node{
    int l,r;
    long long sum;
}t[800010];
int n,prt[200010];
long long s[200010];
void Build(ci x,ci l,ci r){
    t[x].l=l,t[x].r=r;
    if(l==r)return;
    int mid=l+r>>1;
    Build(x<<1,l,mid),Build(x<<1|1,mid+1,r);
}
void Change(ci x,ci id){
    if(t[x].l==t[x].r)return(void)(t[x].sum=id);
    int mid=t[x].l+t[x].r>>1;
    id<=mid?Change(x<<1,id):Change(x<<1|1,id),Upd(x);
}
long long Query(ci x,ci l,ci r){
    if(l>r)return 0;
    if(t[x].l==l&&t[x].r==r)return t[x].sum;
    int mid=t[x].l+t[x].r>>1;
    return r<=mid?Query(x<<1,l,r):(l>mid?Query(x<<1|1,l,r):(Query(x<<1,l,mid)+Query(x<<1|1,mid+1,r)));
}
int l,r,mid;
int main(){
    scanf("%d",&n),Build(1,1,n);
    for(int i=1;i<=n;++i)scanf("%I64d",&s[i]);
    for(int i=n;i>=1;--i){
        l=1,r=n;
        while(l<r)mid=l+r+1>>1,Sum(mid-1)-Query(1,1,mid-1)<=s[i]?l=mid:r=mid-1;
        prt[i]=l,Change(1,l);
    }
    for(int i=1;i<=n;++i)printf("%d ",prt[i]);
    return 0;
}

E.Let Them Slide

AC时间:2h4min,1084分(-1)

题解:

对于每个长度为\(l\)的条\(a\),它能够对应位置\(k\)的数其实是\(a\)中的一段区间\([max(1,k-(w-l)),min(l,k)]\)

由于一些位置可以不对应任何数,我们令\(a_0=a_{l+1}=0\),上面那个区间改为\([max(0,k-(w-l)),min(l+1,k)]\)

然后从小到大枚举位置\(i\),每次至多增加一个位置或者减少一个位置,可以单调队列维护。

但是这样就\(T\)了。

因为事实上上面的算法是\(O(nw)\)的,可以令\(n=w=10^6\),每一个条长度为1。

考虑对于很短的条\(a\),中间有许多位置都可以对应\(a\)中的所有数。

因此我们只要计算出可以对应所有数的区间,区间中的数直接通过差分的办法区间加上条上数的最大值。

而对于剩下部分,数量是\(O(l)\)的。

因此时间复杂度变为\(O(\sum l)\)

code:

讲起来简单,其实有不少细节。

(这就是你一份代码写将近1h的理由?)

#include<bits/stdc++.h>
#define ci const int&
using namespace std;
int n,w,len,t,mx,dq[1000010],id[1000010],hd,tl;
long long d[1000010];
vector<int>a[1000010];
void Add(ci x,ci ind){
    if(ind>=a[x].size())return;
    while(hd>=tl&&dq[hd]<=a[x][ind])--hd;
    dq[++hd]=a[x][ind],id[hd]=ind;
}
void Del(ci x,ci ind){
    if(id[tl]==ind)++tl;
}
int main(){
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;++i){
        scanf("%d",&len),hd=0,tl=1,a[i].push_back(0),mx=0;
        for(int j=1;j<=len;++j)scanf("%d",&t),a[i].push_back(t),mx=max(mx,t);
        a[i].push_back(0),Add(i,0),Add(i,1);
        if(w-len==0)Del(i,0);
        for(int j=1;j<=len+1;Del(i,j-(w-len)),++j,Add(i,j))d[j]+=dq[tl],d[j+1]-=dq[tl];
        if(len+1<w-len)d[len+2]+=mx,d[w-len+1]-=mx,Del(i,0);
        for(int j=max(w-len+1,len+2);j<=w;++j)d[j]+=dq[tl],d[j+1]-=dq[tl],Del(i,j-(w-len));
    }
    for(int i=1;i<=w;++i)d[i]+=d[i-1],printf("%I64d ",d[i]);
    return 0;
}

总结

比赛最后14s AC了E题

还好延时了5min...不然橙名估计就没了

C题是真的自闭,D和E看到题面2min内就想出了正解...QWQ

posted @ 2019-09-09 20:40  xryjr233  阅读(...)  评论(... 编辑 收藏