5.21pkusc模拟赛6

2 篇文章 0 订阅
2 篇文章 0 订阅

A.一直一个长度为n的序列,求对于相邻i,j,abs(a[i]-a[j])<=h的子数列个数%9901(n<=100000,h<=1000000000)(5.21)

首先离散化,然后考虑dp,dp[i]=sigma(dp[j]) (abs(dp[i]-dp[j])<=h) 然后上树状数组优化。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define N 100005
#define Mo 9901
#define ll long long
using namespace std;
int a[N],b[N],c[N],tmp[N],n,h;
ll ans[N];
int lowbit(int x){return x&-x;}
int binary_search1(int l,int r,int x){
    while(l<r){
        int mid=(l+r)>>1;
        if(b[mid]<x) l=mid+1;
        else r=mid;
    }
    return l;
}
int binary_search2(int l,int r,int x){
    while(l<r){
        int mid=(l+r)>>1;
        if(b[mid]<=x) l=mid+1;
        else r=mid;
    }
    return l-1;
}
void add(int pos,int x){
    while(pos<N){
        c[pos]+=x;
        pos+=lowbit(pos);
    }
}
ll getsum(int pos){
    ll ret=0;
    while(pos){
        ret+=c[pos];
        pos-=lowbit(pos);
    }
    return ret;
}
int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    while(~scanf("%d%d",&n,&h)){
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        memset(c,0,sizeof(c));
        memset(tmp,0,sizeof(tmp));
        memset(ans,0,sizeof(ans));
        int cnt=0;
        ll res=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]),tmp[i]=a[i];
        sort(tmp+1,tmp+1+n);
        for(int i=1;i<=n;i++)
            if(tmp[i]!=tmp[i-1])
                b[++cnt]=tmp[i];
        //for(int i=1;i<=n;i++) printf("%d ",b[i]);puts("");
        for(int i=1;i<=n;i++){
            int x=binary_search1(1,cnt+1,max(a[i]-h,0));
            int y=binary_search2(1,cnt+1,a[i]+h);
            int now=binary_search2(1,cnt+1,a[i]);
            //printf("%d %d %d\n",x,y,now);
            ans[i]+=getsum(y)-getsum(x-1);
            ans[i]%=Mo;
            //printf("*%d\n",ans[i]);
            add(now,ans[i]+1);
            res+=ans[i];res%=Mo;
        }
        printf("%lld\n",res);
    }
    return 0;
}

B.求次短路。

正反两次求最短路,每局每条边,设当前枚举边为(u,v,w),则ans=min(ans,dis1[u]+w+dis2[v]) (dis1[u]+w+dis2[v]!=dis1[n])(5.21)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
#define inf 0x7fffffff
#define N 100005
using namespace std;
struct E {int to,nxt,w;}edge[N*2];
int n,r,tot=1;
int dis[2][N],u[N],v[N],w[N],idx[N];
bool vis[N];
queue<int>q;
void addedge(int from,int to,int w){
    edge[tot].to=to;edge[tot].w=w;edge[tot].nxt=idx[from];idx[from]=tot++;
}
void spfa(int s,int t,int o){
    memset(dis[o],127,sizeof(dis[o]));
    memset(vis,0,sizeof(vis));
    dis[o][s]=0;vis[s]=0;q.push(s);
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=0;
        for(int t=idx[x];t;t=edge[t].nxt){
            E e=edge[t];
            if(dis[o][x]+e.w<dis[o][e.to]){
                dis[o][e.to]=dis[o][x]+e.w;
                if(!vis[e.to]) q.push(e.to),vis[e.to]=1;
            }
        }
    }
}
int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&r);
    int x,y,ww;
    for(int i=1;i<=r;i++)
        scanf("%d%d%d",&x,&y,&ww),addedge(x,y,ww),addedge(y,x,ww),u[i]=x,v[i]=y,w[i]=ww;
    spfa(1,n,0);
    spfa(n,1,1);
    int ans=inf;
    for(int i=1;i<=r;i++){
        int x=u[i],y=v[i],ww=w[i];
        if(dis[0][x]+dis[1][y]+ww!=dis[0][n]) 
            ans=min(ans,dis[0][x]+dis[1][y]+ww);
        x=v[i];y=u[i];
        if(dis[0][x]+dis[1][y]+ww!=dis[0][n]) 
            ans=min(ans,dis[0][x]+dis[1][y]+ww);
    }
    printf("%d\n",ans);
    return 0;
}

C.静态区间第k小。(5.23)

听说有种划分树。。写的主席树。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<map>
#include<algorithm>
#define N 100005
using namespace std;
map<int,int>mp;
struct seg{
    int ls,rs,l,r,num;
}t[2400000];// 4*N + NlogN
int root[N],a[N],b[N];
int n,m,cnt;
void init_build(int k,int l,int r){
    t[k].l=l;t[k].r=r;
    if(l==r) return;
    int ls=++cnt,rs=++cnt;
    t[k].ls=ls;t[k].rs=rs;
    int mid=(l+r)>>1;
    init_build(ls,l,mid);
    init_build(rs,mid+1,r);
}
void modify(int k,int x){
    int l=t[k].l,r=t[k].r;
    int mid=(l+r)>>1;
    if(l==r) {t[k].num++;return;} 
    if(x<=mid){
        t[k].num++;
        t[++cnt]=t[t[k].ls];
        t[k].ls=cnt;
        if(t[k].ls) modify(t[k].ls,x);
    }
    if(x>=mid+1){
        t[k].num++;
        t[++cnt]=t[t[k].rs];
        t[k].rs=cnt;
        if(t[k].rs) modify(t[k].rs,x);
    }
}
void query(int a,int b,int k,int &ret){
    int lsa=t[a].ls,lsb=t[b].ls;
    int rsa=t[a].rs,rsb=t[b].rs;
    if(t[a].l==t[a].r) {ret=t[a].l;return;}
    if(t[lsb].num-t[lsa].num>=k)
        query(lsa,lsb,k,ret);
    else query(rsa,rsb,k-(t[lsb].num-t[lsa].num),ret);
}
int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+1+n);cnt=0;
    for(int i=1;i<=n;i++)
        if(!mp.count(b[i])){
            mp[b[i]]=++cnt;
            b[cnt]=b[i];
        }
    cnt=1;root[0]=1;
    init_build(1,1,mp[b[n]]);
    //for(int i=1;i<=20;i++) printf("%d %d %d %d %d %d\n",i,t[i].l,t[i].r,t[i].ls,t[i].rs,t[i].num);
    for(int i=1;i<=n;i++){
        root[i]=++cnt;
        t[root[i]]=t[root[i-1]];
        modify(root[i],mp[a[i]]);
    }
    for(int i=1;i<=m;i++){
        int l,r,k,ret;
        scanf("%d%d%d",&l,&r,&k);
        query(root[l-1],root[r],k,ret);
        printf("%d\n",b[ret]);
    }
    //for(int i=1;i<=35;i++) printf("%d %d %d %d %d %d\n",i,t[i].l,t[i].r,t[i].ls,t[i].rs,t[i].num);
    return 0;
}

D.长度为20的01序列,可以对一个数进行一次操作,操作是这个数及其相邻的数字取反。问这个01序列变成全0序列。保证有解。(5.21)

000101……1左边的0一定要翻,所以就每局从左还是从右翻……

#include<iostream>
#include<cstdio>
using namespace std;
int a[21],b[21],ans1,ans2;
int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    for(int i=1;i<=20;i++) scanf("%d",&a[i]),b[i]=a[i];
    for(int i=1;i<=18;i++){
        if(a[i]==0) continue;
        a[i]^=1;a[i+1]^=1;a[i+2]^=1;
        ans1++;
    }if(a[19]+a[20]==2) ans1++;
    for(int i=20;i>=3;i--){
        if(b[i]==0) continue;
        b[i]^=1;b[i-1]^=1;b[i-2]^=1;
        ans2++;
    }if(b[1]+b[2]==2) ans2++;
    printf("%d\n",min(ans1,ans2));
    return 0;
}

F.在m*n的地图上有一些障碍,求使已知三点连通的最小路径长度。(m,n<=100)(5.21)

最小的路径长度一定是三条路交于一个点,枚举这个点即可。

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define N 105
#define inf 1<<30
using namespace std;
struct node{int x,y;};
int dirx[5]={0,1,-1,0,0};
int diry[5]={0,0,0,-1,1};
int dis[4][N][N];
bool vis[N][N];
char map[N][N];
int n,m;
queue<node>q;
void bfs(int x,int y,int o){
    q.push((node){x,y});
    dis[o][x][y]=0;
    vis[x][y]=1;
    while(!q.empty()){
        node now=q.front();q.pop();
        for(int i=1;i<=4;i++){
            int nx=now.x+dirx[i];
            int ny=now.y+diry[i];
            if(nx&&nx<=n&&ny&&ny<=m&&map[nx][ny]!='X'&&!vis[nx][ny]){
                q.push((node){nx,ny});
                vis[nx][ny]=1;
                dis[o][nx][ny]=dis[o][now.x][now.y]+1;
            }
        }
    }
}
int main(){
//  freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    while(~scanf("%d%d",&n,&m)){
        int x[4],y[4],cnt=0;
        memset(dis,255,sizeof(dis));
        for(int i=1;i<=n;i++){
            scanf("%s",map[i]+1);
            for(int j=1;j<=m;j++)
                if(map[i][j]=='F'||map[i][j]=='J'||map[i][j]=='Q')
                    x[++cnt]=i,y[cnt]=j;
        }
        //for(int i=1;i<=cnt;i++) printf("%d %d\n",x[i],y[i]);
        for(int i=1;i<=3;i++){
            memset(vis,0,sizeof(vis));
            bfs(x[i],y[i],i);
        }
        int res=inf;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++){
                bool flag=0;int ans=0;
                for(int k=1;k<=3;k++){
                    if(dis[k][i][j]==-1) {flag=1;break;}
                    ans+=dis[k][i][j];
                }
                if(!flag) res=min(res,ans-2);
            }
        if(res==inf) printf("Impossible\n");
        else printf("%d\n",res);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值