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]); } }