noip2019集训测试赛(三)

主宰稳场 提交于 2019-11-26 17:47:31

Problem A: string

Time Limit: 5000 ms Memory Limit: 256 MB

Description

给出一个长度为n的S串和一个长度为m的T串,定义Ai=S1S2...SiT1T2...TmSi+1Si+2...SN

若i=0表示T串在S串的前面,若i=n表示S串在T串的前面.

对于一个询问l,r,k,x,y,求字典序最小的Ai,若最小字典序有多个就输出最小的i,若不存在满足条件的i就输出-1.其中i满足l≤i≤r且x≤(i mod k)≤y。

Input

第一行输入字符串S和字符串T和一个整数q,表示q个询问

对于每个询问一共一行5个数l,r,k,x,y

Output

一行一共q个数,表示q个答案

Sample Input
abc d 4
0 3 2 0 0
0 3 1 0 0
1 2 1 0 0
0 1 3 2 2
Sample Output
2 3 2 -1

HINT

对于30%的数据1≤n,m,q≤10^3

对于100%的数据1≤n,m,q≤10^5

Solution

还没写出来。。。大概就是先用LCP排个序然后分个块查询

代码实现比较恶心,到时候再说。

Problem B: mex

Time Limit: 1000 ms Memory Limit: 512 MB

Description

给你一个无限长的数组,初始的时候都为0,有3种操作:

操作1是把给定区间[l,r] 设为1,

操作2是把给定区间[l,r] 设为0,

操作3把给定区间[l,r] 0,1反转。

一共n个操作,每次操作后要输出最小位置的0。

Input

第一行一个整数n,表示有n个操作

接下来n行,每行3个整数op,l,r表示一个操作

Output

共n行,一行一个整数表示答案

Sample Input
3
1 3 4
3 1 6
2 1 3
Sample Output
1
3
1

HINT

对于30%的数据1≤n≤10^3,1≤l≤r≤10^18

对于100%的数据1≤n≤10^5,1≤l≤r≤10^18

Solution

一眼看到l和r数据范围,就离线存下来然后离散化

记得要离散化成三个坐标:l,r,r+1,这样两个询问中间的部分才能被表示出来

考虑用线段树维护l,r区间中0出现的最小位置

然后建两棵线段树,一棵初始化全是0,一棵全是1

修改的时候直接两棵一起改,全部推平;至于反转的时候就两边的节点相互交换就好了。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 1000000000000000001
struct node{
    int l,r;
    int pos;
    int tag;
}t[2000001];
int cnt;
int root[2];
int siz;
void build(int &o,int l,int r,bool mark){
    if(!o)o=++cnt;
    t[o].tag=-1;
    if(l==r){
        if(mark)t[o].pos=l;
        else t[o].pos=siz+1;
        return;
    }
    int mid=(l+r)/2;
    build(t[o].l,l,mid,mark);
    build(t[o].r,mid+1,r,mark);
    t[o].pos=min(t[t[o].l].pos,t[t[o].r].pos);
}
void pushdown(int o,int l,int r){
    int ls=t[o].l,rs=t[o].r;
    if(t[o].tag==-1)return;
    t[ls].tag=t[rs].tag=t[o].tag;
    if(t[o].tag==0){
        t[ls].pos=t[rs].pos=siz+1;
    }
    else {
        t[ls].pos=l;
        t[rs].pos=(l+r)/2+1;
    }
    t[o].tag=-1;
}
void update(int o1,int o2,int l,int r,int L,int R){
    //cout<<o1<<" "<<o2<<endl;
    if(L<=l&&r<=R){
        t[o1].pos=siz+1;
        t[o2].pos=l;
        //cout<<l<<endl;
        t[o1].tag=0;
        t[o2].tag=1;
        return;
    }
    pushdown(o1,l,r);
    pushdown(o2,l,r);
    int mid=(l+r)/2;
    if(L<=mid)update(t[o1].l,t[o2].l,l,mid,L,R);
    if(R>mid)update(t[o1].r,t[o2].r,mid+1,r,L,R);
    t[o1].pos=min(t[t[o1].l].pos,t[t[o1].r].pos);
    t[o2].pos=min(t[t[o2].l].pos,t[t[o2].r].pos);
    //cout<<t[o1].pos<<" "<<t[o2].pos<<endl;
}
void swaps(int &o1,int &o2,int l,int r,int L,int R){
    //cout<<o1<<" "<<o2<<endl;
    if(L<=l&&r<=R){
        swap(o1,o2);
        return;
    }
    pushdown(o1,l,r);
    pushdown(o2,l,r);
    int mid=(l+r)/2;
    if(L<=mid)swaps(t[o1].l,t[o2].l,l,mid,L,R);
    if(R>mid)swaps(t[o1].r,t[o2].r,mid+1,r,L,R);
    t[o1].pos=min(t[t[o1].l].pos,t[t[o1].r].pos);
    t[o2].pos=min(t[t[o2].l].pos,t[t[o2].r].pos);
}
int query(int x){
    return t[root[x]].pos;
}
struct que{
    int opt;
    int l,r;
}p[400001],q[400001];
int lis[400001];
int tot;
int minx=-1;
int pos[400001];
signed main(){
    int n;
    scanf("%lld",&n);
    for(int i=1;i<=n;++i){
        scanf("%lld%lld%lld",&p[i].opt,&p[i].l,&p[i].r);
        lis[++tot]=p[i].l;
        lis[++tot]=p[i].r;
        lis[++tot]=p[i].r+1;
        if(minx==-1)minx=min(p[i].l,p[i].r);
        else minx=min(minx,min(p[i].l,p[i].r));
    }
    sort(lis+1,lis+1+tot);
    siz=unique(lis+1,lis+1+tot)-lis-1;
    //cout<<siz<<endl;
    for(int i=1;i<=siz;++i)pos[i]=lis[i];
    pos[siz+1]=INF;
    for(int i=1;i<=n;++i){
        q[i].opt=p[i].opt;
        q[i].l=(lower_bound(lis+1,lis+1+siz,p[i].l))-lis;
        q[i].r=(lower_bound(lis+1,lis+1+siz,p[i].r))-lis;
        //cout<<q[i].opt<<" "<<q[i].l<<" "<<q[i].r<<endl;
    }
    build(root[0],1,siz,true);
    build(root[1],1,siz,false);
    //cout<<cnt<<endl;
    for(int i=1;i<=n;++i){
        if(minx>1){
            puts("1");
            continue;
        }
        if(q[i].opt==1){
            update(root[0],root[1],1,siz,q[i].l,q[i].r);
        }
        if(q[i].opt==2){
            update(root[1],root[0],1,siz,q[i].l,q[i].r);
        }
        if(q[i].opt==3){
            swaps(root[0],root[1],1,siz,q[i].l,q[i].r);
        }
        printf("%lld\n",pos[query(0)]);
    }
}

Problem C: MST

Time Limit: 2000 ms Memory Limit: 256 MB

Description

给定一个n个点m条边的连通图,保证没有自环和重边。对于每条边求出,在其他边权值不变的情况下,它能取的最大权值,使得这条边在连通图的所有最小生成树上。假如最大权值为无限大,则输出-1。

Input

第一行两个整数n,m,表示n个点m条边

接下来m行,每行3个整数x,y,z,表示节点x和节点y之间有一条长z的边

Output

输出一行m个整数,表示每条边的答案

Sample Input
4 4
1 2 2
2 3 2
3 4 2
4 1 3
Sample Output
2 2 2 1 

HINT

对于30%的数据1≤n≤10^3,1≤m≤3∗10^3

对于100%的数据1≤n,m≤2∗10^5,1≤z≤10^9

Solution

我们先把最小生成树建出来。

然后对于最小生成树外的一条边(x,y),在x~y在最小生成树的路径上找到最大的一条边。为了让它始终都在最小生成树上,这条边改成最大边的权值-1就可以了

同样的,对于(x,y,lca)这个环来说,在最小生成树上的边的值至少也应该是(x,y)的权值-1。然后在最小生成树上的边不断在这些(x,y)中取最小值,然后答案就是最小值-1。

这两个东西是显然的。

那么我们倍增搞一下就可以了。

#include<bits/stdc++.h>
using namespace std;
struct qwq{
    int u,v;
    int w;
    int nxt;
    int id;
}edge[1000001],edge1[1000001];
int fa[1000001];
int findfa(int x){
    return x==fa[x]?x:fa[x]=findfa(fa[x]);
}
bool operator <(qwq a,qwq b){
    return a.w<b.w;
}
int n,m;
bool vis[1000001];
void kruskal(){
    sort(edge1+1,edge1+1+m);
    for(int i=1;i<=n;++i)fa[i]=i;
    int tmp=0;
    for(int i=1;i<=m;++i){
        int u=edge1[i].u,v=edge1[i].v;
        int x=findfa(u),y=findfa(v);
        if(x!=y){
            vis[i]=true;
            fa[y]=x;
            tmp++;
        }
        if(tmp==n-1)break;
    }
}
int cnt=-1;
int head[1000001];
void add(int u,int v,int w,int id){
    edge[++cnt].nxt=head[u];
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].id=id;
    head[u]=cnt;
}
int re[1000001];
void addedge(){
    for(int i=1;i<=m;++i){
        int u=edge1[i].u,v=edge1[i].v,w=edge1[i].w,id=edge1[i].id;
        if(vis[i]){
            add(u,v,w,id);
            add(v,u,w,id);
        }
    }
}
int f[1000001][21];
int maxn[1000001][21];
int dep[1000001];
void dfs(int u,int fa){
    for(int i=1;i<=20;++i){
        f[u][i]=f[f[u][i-1]][i-1];
        maxn[u][i]=max(maxn[u][i-1],maxn[f[u][i-1]][i-1]);
    }
    for(int i=head[u];~i;i=edge[i].nxt){
        int v=edge[i].v,w=edge[i].w,id=edge[i].id;
        if(v!=f[u][0]){
            re[v]=id;
            maxn[v][0]=w;
            f[v][0]=u;
            dep[v]=dep[u]+1;
            dfs(v,u);
        }
    }
}
int LCA(int x,int y,int &lca){
    int ans=0;
    if(dep[x]<dep[y])swap(x,y);
    int depth=dep[x]-dep[y];
    for(int i=0;i<=20;++i){
        if(depth&(1<<i)){
            ans=max(ans,maxn[x][i]);
            x=f[x][i];
        }
    }
    if(x==y){
        lca=x;
        return ans;
    }
    for(int i=20;i>=0;--i){
        if(f[x][i]==f[y][i])continue;
        ans=max(ans,max(maxn[x][i],maxn[y][i]));
        x=f[x][i],y=f[y][i];
    }
    lca=f[x][0];
    ans=max(ans,max(maxn[x][0],maxn[y][0]));
    return ans;
}
int ans[1000001];
void solve(int x,int u,int w){
    x=findfa(x);
    while(dep[x]>dep[u]){
        ans[re[x]]=max(ans[re[x]],w);
        //cout<<re[x]<<endl;
        int y=findfa(f[x][0]);
        fa[x]=y;
        x=findfa(x);
    }
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&edge1[i].u,&edge1[i].v,&edge1[i].w);
        edge1[i].id=i;
        ans[i]=-1;
    }
    kruskal();
    addedge();
    dfs(1,-1);
    for(int i=1;i<=n;++i)fa[i]=i;
    for(int i=1;i<=m;++i){
        int u=edge1[i].u,v=edge1[i].v,w=edge1[i].w;
        int id=edge1[i].id;
        if(!vis[i]){
            int lca;
            ans[id]=LCA(u,v,lca)-1;
            //cout<<i<<endl;
            //cout<<u<<" "<<v<<" "<<lca<<endl;
            //cout<<ans[id]<<endl;
            solve(u,lca,w-1);
            solve(v,lca,w-1);
        }
    }
    for(int i=1;i<=m;++i){
        printf("%d ",ans[i]);
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!