并不对劲的素质四连(一)
并不对劲的片手流还没有醒过来,却要做某铝质紫色大剑的题,感到十分不爽,因此称之为“素质四连”。
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('\n');return;} if(x<0){putchar('-'),x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar('\n'); } 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('\n');return;} if(x<0){putchar('-'),x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar('\n'); } 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('\n');return;} if(x<0){putchar('-'),x=-x;} while(x)ch[++f]=x%10+'0',x/=10; while(f)putchar(ch[f--]); putchar('\n'); } 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 */