并不对劲的片手流还没有醒过来,却要做某铝质紫色大剑的题,感到十分不爽,因此称之为“素质四连”。
d1t1
题意:给一棵n个点的树,q次询问,每次询问一条路径上的点权最小值,n,q<=1e5
做法:不穿衣服的Lca裸题,正常倍增或正常树剖
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<vector>
#define maxn 100010
#define maxm 200010
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(x==0){putchar('0'),putchar('
');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
}
int fir[maxn],nxt[maxm],v[maxm],w[maxn],cnt;
int anc[maxn][21],minw[maxn][21],dep[maxn],n,q;
void ade(int u1,int v1){v[cnt]=v1,nxt[cnt]=fir[u1],fir[u1]=cnt++;}
void getanc(int u)
{
for(int i=1;i<=19;i++)anc[u][i]=anc[anc[u][i-1]][i-1],minw[u][i]=min(minw[u][i-1],minw[anc[u][i-1]][i-1]);
for(int k=fir[u];k!=-1;k=nxt[k])
{
if(v[k]!=anc[u][0])
{
anc[v[k]][0]=u,dep[v[k]]=dep[u]+1,minw[v[k]][0]=min(w[u],w[v[k]]);
getanc(v[k]);
}
}
}
int lca(int x,int y)
{
int ans=min(w[x],w[y]);
if(dep[x]<dep[y])swap(x,y);
for(int i=19;i>=0;i--)if(dep[anc[x][i]]>=dep[y])ans=min(ans,minw[x][i]),x=anc[x][i];
if(x==y)return ans;
for(int i=19;i>=0;i--)
if(anc[x][i]!=anc[y][i])
ans=min(ans,min(minw[x][i],minw[y][i])),y=anc[y][i],x=anc[x][i];
if(x!=y)ans=min(ans,min(minw[x][0],minw[y][0]));
return ans;
}
int main()
{
freopen("min.in","r",stdin);
freopen("min.out","w",stdout);
memset(fir,-1,sizeof(fir));
memset(minw,0x7f,sizeof(minw));
n=read(),q=read();
for(int i=1;i<=n;i++)w[i]=read();
for(int i=1;i<n;i++){int x=read(),y=read();ade(x,y),ade(y,x);}
minw[1][0]=w[1];
getanc(1);
while(q--)
{
int x=read(),y=read();
write(lca(x,y));
}
return 0;
}
d1t2
题意:有一个有n个数的数列,q次询问,每次问所有子区间中异或和第k大的,n<=10^4,q<=10^5,数列中的数<=2^20
做法:求前缀异或和s[i],那么区间[l,r]的异或和就是s[l-1]^s[r],这样可以枚举所有s[i]^s[j],桶排序,询问时直接输出第k个(没想到吧?)
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<vector>
#define maxn 10010
#define maxa 1048576
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(x==0){putchar('0'),putchar('
');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
}
int xo[maxa+1],s[maxn],a[maxn],n,q,cnt;
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;++i)a[i]=read(),s[i]=s[i-1]^a[i];
if(n<=1000)
{
for(int i=0;i<=n;i++)
for(int j=i+1;j<=n;j++)
xo[++cnt]=s[i]^s[j];
sort(xo+1,xo+cnt+1);
while(q--)
{
int x=read();
write(xo[x]);
}
}
else
{
for(int i=0;i<=n-1;++i)for(int j=i+1;j<=n;++j)++xo[s[i]^s[j]];
for(int i=1;i<maxa;++i)xo[i]+=xo[i-1];
while(q--)
{
int x=read(),L=0,R=maxa-1,ans=-1;
while(L<=R)
{
int mid=(L+R)>>1;
if(xo[mid]<x)ans=max(mid,ans),L=mid+1;
else R=mid-1;
}
ans++;
write(ans);
}
}
return 0;
}
d1t3
题意:有n个点,m个条件,每个条件有l,r,v,表示点的编号在[l,r]中的任意两点之间都有一条权值为v的边,求最小生成树
做法:因为标程是并查集,这里就写个不一样的吧。可以先将所有条件按v排序,那么考虑到第i个条件时,会发现区间[li,ri]中所有点还未连通的最好都在此时连通。那么区间[li,ri]就会属于同一个连通块。这个刚好可以用线段树的区间覆盖解决。
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<map>
#include<set>
#include<stack>
#include<vector>
#define maxn 100010
#define mi (l+r>>1)
#define ls (u<<1)
#define rs (u<<1|1)
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
char ch[20];int f=0;
if(x==0){putchar('0'),putchar('
');return;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
}
struct Q{int l,r,v;bool operator <(const Q &z)const{return v<z.v;}}q[maxn];
typedef struct node{int hd,tl,num;}X;
X make_x(int hd1,int tl1,int num1){X tmp;tmp.hd=hd1,tmp.tl=tl1,tmp.num=num1;return tmp;}
X tr[maxn<<2];
int n,m,mk[maxn<<2];
X pu(X x,X y)
{
if(x.hd==0)return y;
if(y.hd==0)return x;
int num=x.num+y.num;
if(x.tl==y.hd)num--;
return make_x(x.hd,y.tl,num);
}
void mark(int u,int l,int r,int k){tr[u].num=1,tr[u].hd=tr[u].tl=k,mk[u]=k;return;}
void pd(int u,int l,int r){if(mk[u]&&l<r)mark(ls,l,mi,mk[u]),mark(rs,mi+1,r,mk[u]),mk[u]=0;}
void build(int u,int l,int r)
{
if(l==r){mark(u,l,r,l);return ;}
build(ls,l,mi),build(rs,mi+1,r),tr[u]=pu(tr[ls],tr[rs]);return;
}
X ask(int u,int l,int r,int x,int y)
{
pd(u,l,r);
if(x<=l&&r<=y){return tr[u];}
if(y<l||r<x)return make_x(0,0,0);
return pu(ask(ls,l,mi,x,y),ask(rs,mi+1,r,x,y));
}
void add(int u,int l,int r,int x,int y,int k)
{
pd(u,l,r);
if(x<=l&&r<=y){mark(u,l,r,k);return;}
if(y<l||r<x)return;
add(ls,l,mi,x,y,k),add(rs,mi+1,r,x,y,k),tr[u]=pu(tr[ls],tr[rs]);return;
}
int main()
{
freopen("kruskal.in","r",stdin);
freopen("kruskal.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].v=read();
sort(q+1,q+m+1);
build(1,1,n);
int ans=0;
for(int i=1;i<=m;i++)
{
X f=ask(1,1,n,q[i].l,q[i].r);
ans+=(f.num-1)*q[i].v;
int tail=q[i].r,L=q[i].r,R=n;
while(L<=R)
{
int mid=(L+R>>1);
X tmp=ask(1,1,n,q[i].l,mid);
if(tmp.num==f.num)tail=max(tail,mid),L=mid+1;
else R=mid-1;
}
add(1,1,n,q[i].l,tail,f.hd);
}
X f=ask(1,1,n,1,n);
if(f.num!=1)puts("-1");
else write(ans);
return 0;
}
/*
10 8
2 5 2
4 7 1
8 8 3
8 10 4
2 5 5
5 7 6
7 10 7
1 2 8
*/