Seq
老师给两个人一个1...n的排列,让大家抄下来,结果小明和李华都各自抄错了一个数,他们决定结合两人的序列弄出原序列。不存在输出“Impossible”,多种可能输出字典序小的。
1<=n<=1e5
题解
一个恶心的模拟,可以发现一个性质:原来是排列,如果抄错一个那么就会有一个数出现两遍。
然后总结一下这道题就是:两个序列有且只有一个错误,且错误只在重复的数中产生。
那么首先分别找到重复的位置(如果多个重复或者没重复就不存在),然后再分别求出少的那个数。
然后开始快乐的分类讨论:
1.两者两个重复的位置都相同:
如果除了这两个位置其他有不一样的地方就错了。
两者重复的数要么是相同的要么就是对方少的那一个,所以前面的位置取一个小的数就好。
2.有一个位置相同
如果除了这三个位置其他有不一样的地方就错了。
相同的位置数也相同,那么这个数一定是原序列的数,可以简单模拟,如果不是那么剩下两个位置都是正确的,但是数又相同显然不可能。
相同的位置数不同,那么必然有一个是错的或者两个都是错的,就看剩下两个位置和另外一个序列的相同位置上的数相不相同,以为另外一个序列上那个位置一定是对的。看代码就好了,我感觉讨论多了,因为有些东西懒得证明就保险一点。
3.没有位置相同
根据只有重复的位置可能错这个性质判断就好了,代码这里有点不保险以为没看第二个序列。
#include<bits/stdc++.h> using namespace std; //两个序列分别有且只有一个错误,错误只在重复的数中产生 const int maxn=100005; int n,a[maxn],b[maxn]; int c1[maxn],c2[maxn]; int pos1[3],pos2[3]; int mex1,mex2; bool opt; void print(){ for(int i=1;i<=n;i++) printf("%d\n",a[i]); exit(0); } void get(int l,int j){//pos1[l]==pos2[j] for(int i=1;i<=n;i++){ if(i==pos1[1]||i==pos1[2]||i==pos2[1]||i==pos2[2]) continue; if(a[i]!=b[i]){ printf("Impossible"); exit(0); } } int k= l==1 ? 2 : 1,p= j==1 ? 2 : 1 ; if(pos1[0]==pos2[0]){//数相同,一定是原序列的数 if(mex1==b[pos1[k]]&&mex2==a[pos2[p]]){ a[pos1[k]]=mex1; print(); } else{ printf("Impossible"); exit(0); } } else { if(a[pos1[k]]==b[pos1[k]]){ if(a[pos2[p]]==b[pos2[p]]){ if(mex1==mex2) { a[pos1[l]]=mex1; print(); } else { printf("Impossible"); exit(0); } } else if(mex1==b[pos1[l]]&&mex2==a[pos2[p]]){ a[pos1[l]]=mex1; print(); } else { printf("Impossible"); exit(0); } } else { if(b[pos1[k]]==mex1&&a[pos2[p]]==b[pos2[p]]){ a[pos1[k]]=mex1; print(); } else { printf("Impossible"); exit(0); } } } } int main(){ freopen("seq.in","r",stdin); freopen("seq.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(!c1[a[i]]) c1[a[i]]++; else if(!opt){ pos1[0]=a[i]; pos1[2]=i; opt=true; } else {//有多个重复 printf("Impossible"); return 0; } } opt=false; for(int i=1;i<=n;i++) { scanf("%d",&b[i]); if(!c2[b[i]]) c2[b[i]]++; else if(!opt){ pos2[0]=b[i]; pos2[2]=i; opt=true; } else { printf("Impossible"); return 0; } } if(!pos1[0]||!pos2[0]){//有一个序列没有重复的数 printf("Impossible"); return 0; } for(int i=1;i<=n;i++)//找到重复的两个位置 if(a[i]==pos1[0]){ pos1[1]=i; break; } for(int i=1;i<=n;i++) if(b[i]==pos2[0]){ pos2[1]=i; break; } for(int i=1;i<=n;i++)if(!c1[i]){mex1=i;break;}//找到序列缺少的那个元素 for(int i=1;i<=n;i++)if(!c2[i]){mex2=i;break;} if(pos1[1]==pos2[1]&&pos1[2]==pos2[2]){//位置相同 for(int i=1;i<=n;i++){ if(i==pos1[1]||i==pos1[2]) continue; if(a[i]!=b[i]){ printf("Impossible"); exit(0); } } //序列相同或者另外一个序列上就是缺的那个 if(mex1<pos1[0]) a[pos1[1]]=mex1; else a[pos1[2]]=mex1; print(); } if(pos1[1]==pos2[1]) get(1,1); if(pos1[2]==pos2[2]) get(2,2); if(pos1[1]==pos2[2]) get(1,2); if(pos1[2]==pos2[1]) get(2,1); //没有位置相同 if(a[pos1[1]]==b[pos1[1]]){ a[pos1[2]]=b[pos1[2]]; print(); } if(a[pos1[2]]==b[pos1[2]]){ a[pos1[1]]=b[pos1[1]]; print(); } printf("Impossible"); }
有点恶心,甚至感觉自己没写对。
Work
有一个公司在一条笔直的公路上,坐标为x。它有n个员工,每个员工有一所住宅坐标为$a_{i}$,这条路还有m个打卡机,坐标为$b_{i}$。每天员工从住宅出发,经过任意一个打卡机打卡,然后再去公司,每个打卡机只能为一个员工打卡。
所有人同时出发,求所有人打卡并达到公司,所经过的最短时间,假设一个人移动一个单位需要一单位时间,打卡不需要时间。
1<=n<=m<=1e5,$1<=a_{i},b_{i},x<=1e9$
题解
考试的时候以为很难,也看出了二分,不过不知道咋check。
那么正解就是先对所有人和打卡机排序,每个人选择他所能选的最左边的打卡机,这个应该是因为既然可以选的话就给后面的人多留一点。(或者sxk dalao的方法就是选择不能交叉,可以证明?)
#include<bits/stdc++.h> using namespace std; #define ll long long const int maxn=100005; const ll oo=2000000000; int n,m,x; int a[maxn],b[maxn]; ll get(int i,int j){return abs(a[i]-b[j])+abs(b[j]-x);} bool check(ll mid){ for(int i=1,j=1;i<=n;i++){ while(j<=m&&get(i,j)>mid) j++; if(j>m) return false; j++; } return true; } int main(){ freopen("work.in","r",stdin); freopen("work.out","w",stdout); scanf("%d%d%d",&n,&m,&x); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); sort(a+1,a+n+1); sort(b+1,b+m+1); ll l=0,r=oo,ans; while(l<=r){ ll mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld",ans); }
Tree
给一个带权连同无向图,对于每条边求一个最大的整数权值k,使得把这条边的权值改成k(其他边权不变),整个图的任意一个最小生成树都有这条边。
无论权值为多少都在生成树上输出-1
1<=n,m<=1e5,$1<=w_{i}<=1e9$
题解
既然和最小生成树有关,那么我们就先搞出一颗最小生成树。考虑一条非树边对在树上两点的路径上的树边有影响,他可以代替这条路径上的任何一条边如果权值满足。那么对于一条树边考虑一直增加它的权值,当权值达到那样一条可以代替它的非树边的权值时,就不能保证一定在最小生成树上了,所以我们得出:对于树边,k=min{能够代替它的非树边权值}-1。
对于非树边,考虑减少它的权值,当他到达一条他可以代替的树边的权值,他就可能在最小生成树上了,那么可以得出:对于非树边,k=max{路径上一条树边的权值}-1.
考虑维护信息,可以想到用树剖,维护非树边权值最小值mi和树边的最大值mx,建树时维护出mx,对于每条非树边都要对路径上的边的mi进行取min。
查询的话对于树边好像不用树剖,因为就是一条边,不过懒得改了。
#include<bits/stdc++.h> using namespace std; const int maxn=100005; const int oo=1000000005; int n,m,cnt; int a[maxn],fa[maxn],size[maxn],dep[maxn],son[maxn]; int aa[maxn],top[maxn],id[maxn]; int root,ls[maxn<<1],rs[maxn<<1],mx[maxn<<1],mi[maxn<<1];//mx:最大树边权 mi:最小非树边权 int tag[maxn<<1];//非树边 vector<pair<int,int> >e[maxn]; bool used[maxn]; struct edge{ int x,y,val,id; }p[maxn],pp[maxn]; template<class T>inline void read(T &x){ x=0;char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} } bool cmp(edge a,edge b){return a.val<b.val;} int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);} void kruskal(){ for(int i=1;i<=n;i++) fa[i]=i; int k=0; for(int i=1;i<=m;i++){ int dx=find(pp[i].x),dy=find(pp[i].y); if(dx!=dy){ k++; fa[dx]=dy; used[pp[i].id]=true; e[pp[i].x].push_back(make_pair(pp[i].y,pp[i].val)); e[pp[i].y].push_back(make_pair(pp[i].x,pp[i].val)); if(k==n-1) break; } } for(int i=1;i<=n;i++) fa[i]=0; } void dfs(int u){ size[u]=1; for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i].first; if(v==fa[u]) continue; dep[v]=dep[u]+1; fa[v]=u; a[v]=e[u][i].second; dfs(v); size[u]+=size[v]; if(size[v]>size[son[u]]) son[u]=v; } } void dfs(int u,int tp){ top[u]=tp; id[u]=++cnt; aa[cnt]=a[u]; if(!son[u]) return ; dfs(son[u],tp); for(unsigned int i=0;i<e[u].size();i++){ int v=e[u][i].first; if(v==fa[u]||v==son[u]) continue; dfs(v,v); } } int min(int x,int y){return x<y ? x : y ;} int max(int x,int y){return x>y ? x : y ;} void update(int rt){ mi[rt]=min(mi[ls[rt]],mi[rs[rt]]); mx[rt]=max(mx[ls[rt]],mx[rs[rt]]); } void build(int &rt,int l,int r){ rt=++cnt; tag[rt]=oo; if(l==r){ mi[rt]=oo; mx[rt]=aa[l]; return ; } int mid=(l+r)>>1; build(ls[rt],l,mid); build(rs[rt],mid+1,r); update(rt); } void put_tag(int rt,int val){ mi[rt]=min(mi[rt],val); tag[rt]=min(tag[rt],val); } void push_down(int rt){ put_tag(ls[rt],tag[rt]); put_tag(rs[rt],tag[rt]); tag[rt]=oo; } void modify(int rt,int l,int r,int a_l,int a_r,int val){ if(a_l<=l&&r<=a_r) { put_tag(rt,val); return ; } if(tag[rt]!=oo) push_down(rt); int mid=(l+r)>>1; if(a_l<=mid) modify(ls[rt],l,mid,a_l,a_r,val); if(mid<a_r) modify(rs[rt],mid+1,r,a_l,a_r,val); update(rt); } void modify(int x,int y,int val){ while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); modify(1,1,n,id[top[y]],id[y],val); y=fa[top[y]]; } if(dep[x]>dep[y]) swap(x,y); modify(1,1,n,id[x]+1,id[y],val); } int querymin(int rt,int l,int r,int a_l,int a_r){ if(a_l<=l&&r<=a_r) return mi[rt]; if(tag[rt]!=oo) push_down(rt); int mid=(l+r)>>1; int ret=oo; if(a_l<=mid) ret=min(ret,querymin(ls[rt],l,mid,a_l,a_r)); if(mid<a_r) ret=min(ret,querymin(rs[rt],mid+1,r,a_l,a_r)); return ret; } int querymin(int x,int y){ int ret=oo; while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); ret=min(ret,querymin(1,1,n,id[top[y]],id[y])); y=fa[top[y]]; } if(dep[x]>dep[y]) swap(x,y); ret=min(ret,querymin(1,1,n,id[x]+1,id[y])); return ret!=oo ? ret-1 : -1; } int querymax(int rt,int l,int r,int a_l,int a_r){ if(a_l<=l&&r<=a_r) return mx[rt]; int mid=(l+r)>>1; int ret=0; if(a_l<=mid) ret=max(ret,querymax(ls[rt],l,mid,a_l,a_r)); if(mid<a_r) ret=max(ret,querymax(rs[rt],mid+1,r,a_l,a_r)); return ret; } int querymax(int x,int y){ int ret=0; while(top[x]!=top[y]){ if(dep[top[x]]>dep[top[y]]) swap(x,y); ret=max(ret,querymax(1,1,n,id[top[y]],id[y])); y=fa[top[y]]; } if(dep[x]>dep[y]) swap(x,y); ret=max(ret,querymax(1,1,n,id[x]+1,id[y])); return ret-1; } int main(){ freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); read(n);read(m); for(int i=1;i<=m;i++){ read(p[i].x);read(p[i].y);read(p[i].val); pp[i]=(edge){p[i].x,p[i].y,p[i].val,i}; } sort(pp+1,pp+m+1,cmp); kruskal(); dfs(1); dfs(1,1); cnt=0; build(root,1,n); for(int i=1;i<=m;i++) if(!used[i]) modify(p[i].x,p[i].y,p[i].val); for(int i=1;i<=m;i++){ if(used[i]) printf("%d\n",querymin(p[i].x,p[i].y)); else printf("%d\n",querymax(p[i].x,p[i].y)); } return 0; } /* 4 4 2 1 2 3 2 2 4 3 2 1 4 3 */