NOIP2017賽前模擬11月4日總結:

第一次掛0·····有點感傷···主要是由於時間分配太不合理了··花2個半小時搞第一題最後還wa完了··第二題很簡單花了30分鐘打完但沒打對拍結果wa完···第三題暴力能夠拿20分的但沒時間打了···node

第一次感覺到了暴力的重要性··第一是想不出正解部分分是要拿的··第二是即便想出正解對拍也要用暴力···ios

之後考試決定遇到一道題先只想個20分鐘·若是想不出正解先把暴力打了··三道題這樣弄完後再去細細想正解

題目1:區間

  給定一個n個正整數的序列··q次詢問兩個數a,b,問序列中有多少個區間使得ab出現次數相等(0次也算),n<=8000,q<=500000數組

  先說暴力的方法··咱們統計完前綴和後找出知足題意的區間lr爲sum[a][r]-sum[a][l-1]=sum[b][r]-sum[b][l-1],移項後爲sum[a][r]-sum[b][r]=sum[a][l-1]-sum[b][l-1],所以對於每次詢問直接開一個桶記錄每一個兩個數每個位置的sum[a][i]-sum[b][i]的值的總數計算便可···順便討論一下ab是否存在於序列中的狀況,注意ab有可能相等優化

  其實正解只是暴力的優化····這道題明顯也想不出什麼巧妙的方法··第一點是咱們能夠發現咱們計算sum[a][i]-sum[b][i]的效果等效於咱們在遍歷每一個位置時記錄一個tag,遇到a的話+1,遇到b的話-1,每一個位置上的tag值實際上就是sum[a][i]-sum[b][i],所以不用統計前綴和直接掃就能夠了··第二點是咱們能夠發現暴力是對於每次詢問咱們是暴力O(n)掃過去的··其實能夠發現中途的不是ab的位置其實是多餘的···所以咱們徹底能夠開個數組記錄每一個數出現的位置(推薦用vector動態開··很方便··考試時腦子抽了寫了個手動數組麻煩死··)對於詢問的兩個數將兩個數的位置數組按序合併而後直接在位置數組上掃就能夠了·····能夠證實優化後的複雜度是n^2的ui

  代碼:spa

#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
const int N=50005;
const int M=5e5+5;
inline int R(){
    char c;int f=0;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0';
    return f;
}
struct node{int x,y;}q[M];
vector<int>pos[N];
int tub[N*2],n,Q,b[N],cnt,num[N],tim,visit[N*2];
inline void lsh(){
    sort(b+1,b+cnt+1);
    cnt=unique(b+1,b+cnt+1)-b-1;
    for(int i=1;i<=n;i++) num[i]=lower_bound(b+1,b+cnt+1,num[i])-b;
}
int main(){
    n=R(),Q=R();            
    for(int i=1;i<=n;i++) num[i]=R(),b[++cnt]=num[i];
    lsh();    
    for(int i=1;i<=Q;i++){
        q[i].x=R(),q[i].y=R();
        int tx=lower_bound(b+1,b+cnt+1,q[i].x)-b;
        int ty=lower_bound(b+1,b+cnt+1,q[i].y)-b;
        if(b[tx]!=q[i].x) q[i].x=0;
        else q[i].x=tx;
        if(b[ty]!=q[i].y) q[i].y=0;
        else q[i].y=ty;    
    }
    for(int i=1;i<=n;i++) pos[num[i]].push_back(i);
    for(register int i=1;i<=Q;i++){
        if((!q[i].x&&!q[i].y)||q[i].x==q[i].y){cout<<(n+1)*n/2<<endl;continue;}
        static int loc[N];int temp=0,a=q[i].x,b=q[i].y;
        register int head1=0,head2=0;
        while(head1<pos[a].size()||head2<pos[b].size()){
            if(head1==pos[a].size()){
                while(head2<pos[b].size()) loc[++temp]=pos[b][head2],head2++;
                continue;
            }
            if(head2==pos[b].size()){
                while(head1<pos[a].size()) loc[++temp]=pos[a][head1],head1++;
                continue;
            }
            if(pos[a][head1]>pos[b][head2]) loc[++temp]=pos[b][head2],head2++;
            else loc[++temp]=pos[a][head1],head1++;
        }
        if(!a||!b){
            long long ans=0;
            for(int i=1;i<=temp;i++) ans+=(loc[i]-loc[i-1]-1)*(loc[i]-loc[i-1])/2;
            ans+=(n-loc[temp])*(n-loc[temp]+1)/2;
            cout<<ans<<endl;
        }
        else{
            tim++;int maxx,minn,tag=N;
            tub[N]=1;visit[N]=tim;maxx=minn=N;long long ans=0;
            for(register int i=1;i<=temp;i++){
                if(num[loc[i]]==a){
                    tag++;maxx=max(maxx,tag),minn=min(minn,tag);
                    if(visit[tag]!=tim) tub[tag]=1,visit[tag]=tim;
                    else tub[tag]++;
                }
                else{
                    tag--;maxx=max(maxx,tag),minn=min(minn,tag);
                    if(visit[tag]!=tim) tub[tag]=1,visit[tag]=tim;
                    else tub[tag]++;
                }
                if(i!=temp)    tub[tag]+=loc[i+1]-loc[i]-1;
                else tub[tag]+=n-loc[i];
            }
            tub[N]+=loc[1]-1;
            for(register int i=minn;i<=maxx;i++)
                if(visit[i]==tim) ans+=tub[i]*(tub[i]-1)/2;
            cout<<ans<<endl;
        }
    }
    return 0;
}

 

題目2:排列

  給定n個數的排列的每一個位置上的數的逆序數的前綴和(逆序數:前面的數中比該數大的數的個數),求出該排列····.net

  很簡單的一道題··咱們先將經過前綴和直接算出每一個數的逆序數(從後往前一次用這個位置的前綴和減去下一個位置的前綴和考試的時候我是從前日後減的坑爹樣例沒有看出錯),這時咱們從後往前以此處理···很明顯對於每一個位置的逆序數a,咱們知道它前面的數中有多少個數比它大從而知道了它的排名··直接用線段樹區間查詢k大值就能夠了,再將它刪除code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
inline long long R(){
    char c;long long f=0;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c<='9'&&c>='0';c=getchar()) f=f*10+c-'0';
    return f;
}
const int N=1e5+5;
int tree[N*4],n,anss[N],temp;
long long num[N];
inline void build(int k,int l,int r){
    if(l==r){
        tree[k]++;return;
    }
    int mid=(l+r)/2;
    build(k*2,l,mid);build(k*2+1,mid+1,r);
    tree[k]=tree[k*2]+tree[k*2+1];
}
inline void find(int k,int l,int r,int x){
    if(l==r){
        tree[k]--;temp=l;return;
    }
    int mid=(l+r)/2;
    if(x<=tree[k*2]) find(k*2,l,mid,x);
    else find(k*2+1,mid+1,r,x-tree[k*2]);
    tree[k]=tree[k*2]+tree[k*2+1];
}
int main(){
    //freopen("premu.in","r",stdin);
    //freopen("premu.out","w",stdout);
    n=R();
    for(int i=1;i<=n;i++) num[i]=R();
    for(int i=n;i>=1;i--) num[i]-=num[i-1];
    build(1,1,n);
    for(int i=n;i>=1;i--){
        find(1,1,n,i-num[i]);
        anss[i]=temp;
    }
    for(int i=1;i<=n;i++) cout<<anss[i]<<" ";
    return 0;
}

 

題目3:邊的處理

  有一個n個點的無向圖,給出m條邊,每條邊的信息形如x,y,c,r
  給出q組詢問形如u,v,l,r
  接下來解釋詢問以及邊的意義。 
  詢問表示,一開始你在點u上,而後按順序處理編號從l到r的邊。 
  對於一條邊xycr,你能夠進行兩次操做: 
  一、若是你當前在x點或者y點上,那麼你能夠走這條邊(從x到y或從y到x)並付出c的代價(固然你也能夠不走,看操做2)。 
  二、若是你不走這條邊或者不能夠走這條邊(即你當前不在x或y上),那麼你須要付出r的代價。 
  詢問若是要從點u開始,按順序處理完編號從l到r的邊以後到達點v的最小代價,若是不能到達u,那麼輸出-1
  n<=30,m<=20000,q<=200000
blog

  很好的分治題···rem

  暴力的話咱們考慮對於每次詢問的話每次處理到某一條邊時所在點的全部的狀況而後想最短路同樣更新它到起點的距離

  分治的話挺複雜的,具體見http://blog.csdn.net/qq_35649707/article/details/78439108,%%%%%%%%%

  代碼:

  

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<algorithm>
#include<cstring>
using namespace std;
const int M=2e4+5;
const int N=2e5+5;
const int inf=0x3f3f3f3f;
inline int R(){
    char c;int f=0;
    for(c=getchar();c<'0'||c>'9';c=getchar());
    for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0';
    return f;
}
struct node{
    int x,y,c,r,u,v,l,id;
}ed[M],q[N];
int n,m,Q,f[M][35][35],anss[N];
inline void solve(int l,int r,int x,int y){
    if(x>y)    return;
    if(l==r){
        if(ed[l].x>ed[l].y) swap(ed[l].x,ed[l].y);
        for(int i=x;i<=y;i++){
            if(q[i].u>q[i].v) swap(q[i].u,q[i].v);
            if(q[i].l==l&&q[i].u==ed[l].x&&q[i].v==ed[l].y)    anss[q[i].id]=ed[l].c;
            if(q[i].u==q[i].v) anss[q[i].id]=min(anss[q[i].id],ed[l].r);
        }
        return;
    }
    static node d[N];int ri=y+1,le=x-1,mid=(l+r)/2;
    memset(f[mid],inf,sizeof(f[mid]));
    f[mid][ed[mid].x][ed[mid].y]=f[mid][ed[mid].y][ed[mid].x]=ed[mid].c;
    for(int i=1;i<=n;i++) f[mid][i][i]=min(f[mid][i][i],ed[mid].r);
    for(int i=mid-1;i>=l;i--){
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                f[i][j][k]=f[i+1][j][k]+ed[i].r;
        for(int j=1;j<=n;j++) f[i][ed[i].x][j]=min(f[i][ed[i].x][j],f[i+1][ed[i].y][j]+ed[i].c);
        for(int j=1;j<=n;j++) f[i][ed[i].y][j]=min(f[i][ed[i].y][j],f[i+1][ed[i].x][j]+ed[i].c);
    }
    memset(f[mid+1],inf,sizeof(f[mid+1]));
    f[mid+1][ed[mid+1].x][ed[mid+1].y]=f[mid+1][ed[mid+1].y][ed[mid+1].x]=ed[mid+1].c;
    for(int i=1;i<=n;i++) f[mid+1][i][i]=min(f[mid+1][i][i],ed[mid+1].r);
    for(int i=mid+2;i<=r;i++){
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                f[i][j][k]=f[i-1][j][k]+ed[i].r;
        for(int j=1;j<=n;j++) f[i][j][ed[i].x]=min(f[i][j][ed[i].x],f[i-1][j][ed[i].y]+ed[i].c);
        for(int j=1;j<=n;j++) f[i][j][ed[i].y]=min(f[i][j][ed[i].y],f[i-1][j][ed[i].x]+ed[i].c);
    }
    for(int i=x;i<=y;i++){
        if(q[i].l<=mid&&q[i].r>mid){
            anss[q[i].id]=inf;
            for(int j=1;j<=n;j++) anss[q[i].id]=min(f[q[i].l][q[i].u][j]+f[q[i].r][j][q[i].v],anss[q[i].id]); 
        }
        else if(q[i].r<=mid) d[++le]=q[i];
        else d[--ri]=q[i];
    }
    for(int i=x;i<=le;i++) q[i]=d[i];
    for(int i=ri;i<=y;i++) q[i]=d[i];
    solve(l,mid,x,le);solve(mid+1,r,ri,y);
}
int main(){
    //freopen("a.in","r",stdin);
    n=R(),m=R(),Q=R();memset(anss,inf,sizeof(anss));
    for(int i=1;i<=m;i++) ed[i].x=R(),ed[i].y=R(),ed[i].c=R(),ed[i].r=R();
    for(int i=1;i<=Q;i++) q[i].u=R(),q[i].v=R(),q[i].l=R(),q[i].r=R(),q[i].id=i;
    solve(1,m,1,Q);
    for(int i=1;i<=Q;i++){
        if(anss[i]==inf||anss[i]<0) cout<<"-1"<<endl;  
        else cout<<anss[i]<<endl;
    }
    return 0;
}
相關文章
相關標籤/搜索