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