noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)

fengwu2005發表於2021-06-22

這次考試還是挺好的

畢竟第一題被我給A了,也怪這題太簡單,規律一眼就看出來了,但是除了第一題,剩下的我只有30pts,還是菜

第二題不知道為啥我就直接幹到樹套樹了,線段樹套上一個權值線段樹,然後我發現自己跑得特別慢,

然後就手打了一個超級大暴力,然後就很懵逼的發現,我的暴力比我樹套樹還快十倍

我就很生氣,回去算了一遍複雜度,沒錯是nlog2n,然後我就懷疑自己打假了,直接把自己的暴力程式交上去了

然後成功的30分,然後就人傻了,然後我發現,其實我樹套樹有35pts,但是正解是二分/主席書/顏色權值線段樹都行,

然後我就為了鍛鍊我的二分能力,就去手打了二分,但是大佬們都是lower_bound/upper_bound害,人家只有20來行

第三題我連寫都沒寫,為啥呢,因為這個K吧資料範圍我看都沒看,以為他至少也得1e5

然後我就對著題面看到考試結束,然後我就0分了。。。只有 一句話,我腦殘

一直以為最後一個題是dp然而我dp方程根本想不出來,好像有點像區間dp的樣子

最後發現是貪心,前40分還特別好拿。。。。。

 

洛谷題面:

斐波那契

數顏色

分組

 

T1斐波那契

為什麼說這個題水???

自己去倒一倒,你就發現每天新出生的兔子數量就是一個嚴格的斐波那契數列,

那這樣的話,我們只需在前面在加上一個1,然後對這個序列做一個字首和,就是每天擁有的兔子量(其實字首和後的數列還是一個斐波那契數列)

然後題目要求,每個兔子的編號是按照爸爸的編號大小來排列的,所以我們用要去尋找的兔子的編號,找到他在出生的時候的次序,然後,這個次序就是他爸爸的編號

這個過程的實現就是lower_bound一下,再-1然後用編號減去那一項的值,這樣一直減下去,得到的就是他的祖先

我們拿著兩隻兔子,然後,就像求lca一樣,讓編號大的先跳,一直跳到相等,就找到了

記得做數列題的時候,先吧這個數列輸出一下,不然你不會知道,斐波那契數列的第60項已經1e12了

noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const ll maxn=1e12;
int n;
ll fi[105],fro[105];
ll find(ll x){
    return lower_bound(fro+1,fro+64,x)-fro;
}
ll lca(ll x,ll y){
    if(x<y)swap(x,y);
    while(x!=y){
        x-=fro[find(x)-1];
        if(x<y)swap(x,y);
    }
    return x;
}
signed main(){
    fi[1]=fi[2]=1;
    fro[0]=1;fro[1]=2;fro[2]=3;
    for(re i=3;i<=65;i++){
        fi[i]=fi[i-1]+fi[i-2];
        fro[i]=fro[i-1]+fi[i];
    }
    scanf("%d",&n);
    for(re i=1;i<=n;i++){
        ll a,b;
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",lca(a,b));
    }
}
T1(考場AC)

 

T2數顏色

不行為了不辜負我在考場上的一片苦心,我決定把握的暴力O(nm)和樹套樹O(nlog2n)程式碼粘到這裡。。。

noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3e5+10;
int n,m;
int a[N],ans;
signed main(){
    //freopen("color.out","w",stdout);
    //int o=clock();
    scanf("%d%d",&n,&m);
    for(re i=1;i<=n;i++)scanf("%d",&a[i]);
    for(re i=1;i<=m;i++){
        int typ,l,r,c;
        scanf("%d",&typ);
        if(typ==1){
            scanf("%d%d%d",&l,&r,&c);
            ans=0;
            for(re i=l;i<=r;i++)if(a[i]==c)ans++;
            printf("%d\n",ans);
        }
        else {
            scanf("%d",&l);
            swap(a[l],a[l+1]);
        }
    }
    //cout<<"sb"<<endl;
    //cout<<endl<<endl<<clock()-o<<endl;
}
超級大暴力
noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3e5+10;
int n,m;
int a[N],col[N*2],l[N*2],r[N*2],typ[N*2];
int lsh[N*3],lh;
int C,ans;
struct node{
    int seg;
    int sum[N*80],ls[N*80],rs[N*80];
    void ins(int &x,int l,int r,int pos){
        if(!x)x=++seg;
        if(l==r){
            sum[x]=1;
            return ;
        }
        int mid=l+r>>1;
        if(pos<=mid)ins(ls[x],l,mid,pos);
        else ins(rs[x],mid+1,r,pos);
    }
    int query(int x,int l,int r,int pos){
        if(!x)return 0;
        if(l==r)return sum[x];
        int mid=l+r>>1;
        if(pos<=mid)return query(ls[x],l,mid,pos);
        else return query(rs[x],mid+1,r,pos);
    }
    void merge(int x,int y,int &z){
        if(!x||!y){
            z=x+y;
            return ;
        }
        if(!z)z=++seg;
        sum[z]=sum[x]+sum[y];
        if(ls[z]==ls[y]||ls[z]==ls[x])ls[z]=0;
        merge(ls[x],ls[y],ls[z]);
        if(rs[z]==rs[y]||rs[z]==rs[x])rs[z]=0;
        merge(rs[x],rs[y],rs[z]);
    }
}xds;
int rt[N*4],ir[N];
void build(int x,int l,int r){
    if(l==r){
        ir[l]=x;
        xds.ins(rt[x],1,C,a[l]);
        return ;
    }
    int mid=l+r>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    //cout<<x<<endl;
    xds.merge(rt[x<<1],rt[x<<1|1],rt[x]);
    //cout<<x<<endl;
}
void update(int x,int l,int r,int ql,int qr){
    if(l==r)return ;
    int mid=l+r>>1;
    if(ql<=mid)update(x<<1,l,mid,ql,qr);
    if(qr>mid)update(x<<1|1,mid+1,r,ql,qr);
    //if(qr<=mid||ql>mid)return ;
    xds.merge(rt[x<<1],rt[x<<1|1],rt[x]);
}
void find(int x,int l,int r,int ql,int qr,int co){
    if(ql<=l&&r<=qr){
        ans+=xds.query(rt[x],1,C,co);
        return ;
    }
    int mid=l+r>>1;
    if(ql<=mid)find(x<<1,l,mid,ql,qr,co);
    if(qr>mid)find(x<<1|1,mid+1,r,ql,qr,co);
}
signed main(){
    //freopen("color.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(re i=1;i<=n;i++){
        scanf("%d",&a[i]);
        lsh[++lh]=a[i];
    }
    for(re i=1;i<=m;i++){
        scanf("%d",&typ[i]);
        if(typ[i]==1){
            scanf("%d%d%d",&l[i],&r[i],&col[i]);
            lsh[++lh]=col[i];
        }
        else scanf("%d",&l[i]);
    }
    sort(lsh+1,lsh+lh+1);
    lh=unique(lsh+1,lsh+lh+1)-lsh-1;C=lh;
    for(re i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+lh+1,a[i])-lsh;
    build(1,1,n);
    for(re i=1;i<=m;i++){
        if(typ[i]==1){
            int tmp=col[i];
            col[i]=lower_bound(lsh+1,lsh+lh+1,col[i])-lsh;
            if(tmp!=lsh[col[i]]){
                printf("0\n");
                continue;
            }
            ans=0;find(1,1,n,l[i],r[i],col[i]);
            printf("%d\n",ans);
        }
        else {
            //cout<<l[i]<<" "<<l[i]+1<<endl;
            swap(rt[ir[l[i]]],rt[ir[l[i]+1]]);
            update(1,1,n,l[i],l[i]+1);
        }
    }
    //cout<<xds.seg<<endl;
}
樹套樹

不多說了,直接上正解。

正解就是直接將這個顏色序列按照  先顏色,後位置 的雙關鍵字排序

然後我們就可以直接利用二分,在每一個顏色塊內尋找這個顏色的數量

所以更改的時候,我們發現,兩個顏色的交換不會影響到各個顏色在各自的塊內的位置

所以我們找到這兩個位置,將他們的位置資訊交換就好,仍然滿足單調性

但是注意,同顏色的就不要換了,換了就不對了,我因為這WA了好幾遍

noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=3e5+5;
int n,m;
int a[N],pl[N],pr[N];
struct node{
    int col,pos;
    bool operator < (node a) const {
        if(col!=a.col)return col<a.col;
        return pos<a.pos;
    }
}rib[N];
signed main(){
    scanf("%d%d",&n,&m);
    for(re i=1;i<=n;i++){
        scanf("%d",&a[i]);
        rib[i].col=a[i];
        rib[i].pos=i;
    }
    sort(rib+1,rib+n+1);
    pl[rib[1].col]=1;
    for(re i=1;i<=n;i++){
        if(rib[i].col!=rib[i-1].col){
            pr[rib[i-1].col]=i-1;
            pl[rib[i].col]=i;
        }
    }
    pr[rib[n].col]=n;
    //for(re i=1;i<=3;i++)cout<<pl[i]<<" "<<pr[i]<<endl;
    for(re i=1;i<=m;i++){
        int typ,x,y,z;
        scanf("%d",&typ);
        if(typ==1){
            scanf("%d%d%d",&x,&y,&z);
            int ans=0,l=pl[z]-1,r=pr[z],mid;
            while(l<r){
                mid=l+r+1>>1;
                if(rib[mid].pos<x)l=mid;
                else r=mid-1;
            }
            int L=l;
            //if(rib[l].pos<x)L++;
            r=pr[z];
            while(l<r){
                mid=l+r+1>>1;
                if(rib[mid].pos<=y)l=mid;
                else r=mid-1;
            }
            printf("%d\n",l-L);
        }
        else{
            scanf("%d",&x);
            y=x+1;
            if(a[x]==a[y])continue;
            int l=pl[a[x]],r=pr[a[x]],mid;
            while(l<r){
                mid=l+r+1>>1;
                if(rib[mid].pos<=x)l=mid;
                else r=mid-1;
            }
            rib[l].pos=y;
            l=pl[a[x+1]];r=pr[a[x+1]];
            while(l<r){
                mid=l+r+1>>1;
                if(rib[mid].pos<=y)l=mid;
                else r=mid-1;
            }
            rib[l].pos=x;
            swap(a[x],a[x+1]);
        }
    }
}
T2正解

只有我一個人用手打的二分嗎,,哇嗚嗚。。

T3分組

其實說實話,我挺生氣的,為什麼不把資料範圍放到題面裡,那樣好歹我也有40分,不至於連程式碼都沒打

K=1或2,氣人

分情況討論唄:::

當K=1時,我們可以直接貪心嘛,反正塊都得是連續的,但是你發現,這字典序很難搞啊,但是我們可以直接從後向前列舉,然後得到的分割點就是最優解啊

當K=2時,我們也還是貪心,也是從後往前列舉,延續上一種情況的做法

但是我們如果分成兩組,將不能共存的兩個數看作是連邊的兩個點,然後這個問題就是要我們找到一個最大的二分圖

雖然這個知識點我不會,但是我可以用並查集解決這個問題,還記不記得這個題------關押罪犯

我們可以直接利用並查集判斷這個點可不可以加進去,

還要注意一個特例,兩個數相等的時候,並且這個數的2倍是一個平方數,然後我們只要判斷一下,就可以過掉這個題了

還有,如何利用並查集判斷,那我們就將敵人放到x+i,這樣可以防止查詢的紊亂,所以我因為這個WA了好多變;

noip模擬9[斐波那契·數顏色·分組](洛谷模擬測試)
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=131075;
int n,k;
int a[N];
int ans,sum,dv[N],vis[N*5];
void get_ans1(){
    dv[0]=n;
    for(re i=n;i>=1;i--){
    //    cout<<i<<endl;
        bool pd=false;
        for(re j=sqrt(a[i]);j<=512;j++){
            //cout<<j<<endl;
            if(j*j<a[i])continue;
            if(vis[j*j-a[i]]){
                pd=true;break;
            }
        }
        if(pd==true){
            ans++;
            dv[++sum]=i;
            //cout<<sum<<" "<<dv[sum-1]<<" "<<i<<endl;
            for(re j=dv[sum-1];j>i;j--)vis[a[j]]=0;//cout<<j<<endl;
        }
        vis[a[i]]=1;
    }
}
int fa[N*3],ops[N*3];
int maxx=131100;
int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void get_ans2(){
    for(re i=1;i<=512*512;i++)fa[i]=i;
    dv[0]=n;
    for(re i=n;i>=1;i--){
        //cout<<i<<endl;
        bool pd=false;
        if(vis[a[i]]==1&&(int)sqrt(a[i]*2)*(int)sqrt(a[i]*2)==a[i]*2){
            for(re j=sqrt(a[i]);j<=512;j++){
                if(j*j<a[i])continue;
                if(vis[j*j-a[i]]&&j*j!=a[i]*2){
                    pd=1;break;
                }
            }    
        }
        else{
            for(re j=sqrt(a[i]);j<=512;j++){
                if(j*j<a[i])continue;
                int y=j*j-a[i];
                if(vis[y]==2&&((int)sqrt(2*y)*(int)sqrt(2*y)==2*y)){
                    pd=1;break;
                }
                if(vis[y]){
                    if(y==a[i])continue;
                    else{
                        int fy=find(y),fi=find(a[i]);
                        int gy=find(y+maxx),gi=find(a[i]+maxx);
                        if(fy==fi){
                            pd=1;break;
                        }
                        else{
                            fa[gy]=fi;
                            fa[gi]=fy;
                        }
                    }
                }
            }
        }
        if(pd==true){
            ans++;
            dv[++sum]=i;
            for(re j=dv[sum-1];j>i;j--){
                vis[a[j]]=0;
                fa[a[j]]=a[j];
                fa[a[j]+maxx]=a[j]+maxx;
            }
        }
        vis[a[i]]++;
    }
}
signed main(){
    scanf("%d%d",&n,&k);
    for(re i=1;i<=n;i++)scanf("%d",&a[i]);
    //cout<<a[913]<<" "<<a[914]<<" "<<a[915]<<endl;
    if(k==1)get_ans1();
    else get_ans2();
    printf("%d\n",ans+1);
    for(re i=sum;i;i--)printf("%d ",dv[i]);
    printf("\n");
}
T3

 

相關文章