两次考试
10.22
T1
异或支持一些律,把后面的移到前面就行了
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; const int N=100006; inline void readint(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } int n; int f[N],g[N]; int main(){ //freopen("T1.in","r",stdin); //freopen("T1.out","w",stdout); rint i,j; readint(n); for(i=0;i<n;++i) readint(f[i]); for(i=0;i<n;++i) readint(g[i]); int tt=0; for(i=0;i<n;++i) { tt=(tt^(f[i]^g[i])); printf("%d ",tt); } }
T2
对于只有1、2的情况
ans就是最底下一层最中间开始数到左右两边最近的连续的一段
推广到1~n
可以二分一个值,把比它小的设成1,大于等于的设成2,就可以O(n)求出来结果
是1,说明ans比二分的这个值小
否则,就大于等于
其实数据比较水,对中间三个排序,输出中位数就A了...
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; const int N=100006; inline void readint(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } int n; int f[N],g[N]; int main(){ //freopen("T1.in","r",stdin); //freopen("T1.out","w",stdout); rint i,j; readint(n); for(i=0;i<n;++i) readint(f[i]); for(i=0;i<n;++i) readint(g[i]); int tt=0; for(i=0;i<n;++i) { tt=(tt^(f[i]^g[i])); printf("%d ",tt); } }
T3
考试有思想,但是没打出来
先把T2的边在T1上进行线段覆盖
如果T1上有只覆盖1层的,就可以用它来满足覆盖它的那个边
都满足就YES,否则NO
#pragma GCC optimize("O3") #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #define ll long long #define mem(a,b) memset(a,b,sizeof(a)) #define rint register int using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int N=200006; const int INF=1000000000; int first[N],nt[N],ver[N],e; inline void addbian(int u,int v) { ver[e]=v; nt[e]=first[u]; first[u]=e++; } int T; int n; int u[N],v[N]; int mn[N*5],sum[N*5],jiid[N*5],jiv[N*5]; int L,R,oo,vv,pos,id; inline void add(int l,int r,int x) { if(L<=l&&r<=R) { mn[x]+=vv; sum[x]+=(oo*(r-l+1)); jiv[x]+=vv; jiid[x]+=oo; return ; } int mid=(l+r)>>1,ls=x<<1,rs=x<<1|1; if(jiid[x]) { jiid[ls]+=jiid[x]; jiid[rs]+=jiid[x]; sum[ls]+=jiid[x]*(mid-l+1); sum[rs]+=jiid[x]*(r-mid); jiid[x]=0; } if(jiv[x]) { jiv[ls]+=jiv[x]; jiv[rs]+=jiv[x]; mn[ls]+=jiv[x]; mn[rs]+=jiv[x]; jiv[x]=0; } if(L<=mid) add(l,mid,ls); if(mid<R) add(mid+1,r,rs); mn[x]=(mn[ls]<mn[rs]?mn[ls]:mn[rs]); sum[x]=sum[ls]+sum[rs]; } inline void qq(int l,int r,int x) { if(l==r) { if(mn[x]==1) mn[x]=INF,pos=l,id=sum[x]; return ; } int mid=(l+r)>>1,ls=x<<1,rs=x<<1|1; if(jiid[x]) { jiid[ls]+=jiid[x]; jiid[rs]+=jiid[x]; sum[ls]+=jiid[x]*(mid-l+1); sum[rs]+=jiid[x]*(r-mid); jiid[x]=0; } if(jiv[x]) { jiv[ls]+=jiv[x]; jiv[rs]+=jiv[x]; mn[ls]+=jiv[x]; mn[rs]+=jiv[x]; jiv[x]=0; } if(mn[ls]==1) qq(l,mid,ls); else qq(mid+1,r,rs); mn[x]=(mn[ls]<mn[rs]?mn[ls]:mn[rs]); sum[x]=sum[ls]+sum[rs]; } int fa[N],dep[N],size[N],son[N]; inline void dfs1(int x) { size[x]=1; for(int i=first[x];i!=-1;i=nt[i]) { if(ver[i]==fa[x]) continue; fa[ver[i]]=x; dep[ver[i]]=dep[x]+1; dfs1(ver[i]); size[x]+=size[ver[i]]; if(size[son[x]]<size[ver[i]]) son[x]=ver[i]; } } int top[N],zheng[N],timer; inline void dfs2(int x,int tp) { top[x]=tp; zheng[x]=++timer; if(son[x]) dfs2(son[x],tp); for(int i=first[x];i!=-1;i=nt[i]) { if(ver[i]==fa[x]||ver[i]==son[x]) continue; dfs2(ver[i],ver[i]); } } inline void ADD(int x,int y) { int fx=top[x],fy=top[y]; while(fx!=fy) { if(dep[fx]<dep[fy]) { x^=y; y^=x; x^=y; fx^=fy; fy^=fx; fx^=fy; } L=zheng[fx]; R=zheng[x]; add(1,n,1); x=fa[fx]; fx=top[x]; } if(dep[x]>dep[y]) x^=y,y^=x,x^=y; if(x!=y) L=zheng[x]+1,R=zheng[y],add(1,n,1); } inline int work() { mem(ver,0); mem(first,-1); mem(nt,0); e=0; mem(son,0); mem(size,0); timer=0; mem(mn,0); mem(sum,0); mem(jiv,0); mem(jiid,0); rint i,j; int tin1,tin2; for(i=1;i<n;++i) { read(tin1); read(tin2); addbian(tin1,tin2); addbian(tin2,tin1); } for(i=1;i<n;++i) read(u[i]), read(v[i]); fa[1]=-1; dep[1]=0; dfs1(1); dfs2(1,1); L=1; R=1; vv=INF; oo=0; add(1,n,1); for(i=1;i<n;++i) oo=i,vv=1,ADD(u[i],v[i]); for(i=1;i<n;++i) { id=0; pos=0; qq(1,n,1); if(!id) return 0; oo=-id; vv=-1; ADD(u[id],v[id]); } return 1; } int main(){ //freopen("T3.in","r",stdin); //freopen("tree13.in","r",stdin); read(T); while(T--) { read(n); if(work()) printf("YES\n"); else printf("NO\n"); } }
10.23
T1
有两种做法
(1)
二分答案,用并查集维护,复杂度$O(n^2logn)$
当然,你需要韩门神的疯狂剪枝和超小常数...
(2)
对每一个星星和上下两个边界进行两两连边,边权是他们之间的距离
跑最小生成树(prim),再找从上边界到下边界的路径上的最小值/2即可
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <cmath> #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define dd double #define rint register int //#define dis(x3,y3,x4,y4) (sqrt((y3-y4)*(y3-y4)+(x3-x4)*(x3-x4))) #define esp 0.0000001 using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int MAXK=10006; int first[MAXK*3],nt[MAXK*3],ver[MAXK*3],e; dd w[MAXK*3]; void addbian(int u,int v,dd _w) { ver[e]=v; w[e]=_w; nt[e]=first[u]; first[u]=e++; } int n,m,K; int x[MAXK+100],y[MAXK+100]; dd mn[MAXK+100]; bool ff[MAXK+100]; int pr[MAXK+100]; inline dd dis(int f1,int f2) { if( (f1==0&&f2==K+1)||(f1==K+1&&f2==0) ) return m; if(f1==0) return y[f2]; if(f1==K+1) return m-y[f2]; if(f2==0) return y[f1]; if(f2==K+1) return m-y[f1]; return sqrt((y[f1]-y[f2])*(y[f1]-y[f2])+(x[f1]-x[f2])*(x[f1]-x[f2])); } dd an[MAXK+100]; void dfs(int x) { for(int i=first[x];i!=-1;i=nt[i]) { if(an[ver[i]]>w[i]) an[ver[i]]=w[i]; dfs(ver[i]); } } dd work() { rint i,j; int all=K+1; for(i=all+1;i>=0;--i) mn[i]=an[i]=0x7fffffff; mn[0]=0; int tt; dd vv; for(i=0;i<=all;++i) { tt=all+1; for(j=0;j<=all;++j) if(!ff[j]&&mn[tt]>mn[j]) tt=j; ff[tt]=1; //printf("i=%d tt=%d\n",i,tt); for(j=0;j<=all;++j) if(!ff[j]) { vv=dis(tt,j); //printf("j=%d vv=%lf\n",j,vv); if(mn[j]>vv) mn[j]=vv,pr[j]=tt; } } for(i=1;i<=all;++i) addbian(pr[i],i,mn[i]); dfs(0); return an[all]/2.0; } int main(){ freopen("T1.in","r",stdin); //freopen("T1.out","w",stdout); mem(first,-1); rint i,j; read(n); read(m); read(K); for(i=1;i<=K;++i) read(x[i]),read(y[i]); printf("%.8lf",work()); }
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <cmath> #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define dd double #define rint register int //#define dis(x3,y3,x4,y4) (sqrt((y3-y4)*(y3-y4)+(x3-x4)*(x3-x4))) #define esp 0.0000001 using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } inline void readll(ll &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int MAXK=10006; int first[MAXK*3],nt[MAXK*3],ver[MAXK*3],e; ll w[MAXK*3]; void addbian(int u,int v,ll _w) { ver[e]=v; w[e]=_w; nt[e]=first[u]; first[u]=e++; } int n,m,K; ll x[MAXK+100],y[MAXK+100]; ll mn[MAXK+100],ans; bool ff[MAXK+100]; int pr[MAXK+100]; inline ll dis(int f1,int f2) { if(f1>f2) f1^=f2,f2^=f1,f1^=f2; if(f1==K+1&&f2==K+2) return (ll)m*m; if(f2==K+1) return y[f1]*y[f1]; if(f2==K+2) return ((ll)m-y[f1])*((ll)m-y[f1]); return (y[f1]-y[f2])*(y[f1]-y[f2])+(x[f1]-x[f2])*(x[f1]-x[f2]); } void dfs(int x,int fa,ll ww) { if(x==K+2) { ans=ww; return ; } for(int i=first[x];i!=-1;i=nt[i]) { if(ver[i]==fa) continue; dfs(ver[i],x,max(ww,w[i])); } } dd work() { rint i,j; int all=K+2; mem(mn,0x7f); mn[K+1]=0; int tt; ll vv; for(i=1;i<=all;++i) { tt=0; for(j=1;j<=all;++j) if(!ff[j]&&mn[tt]>mn[j]) tt=j; ff[tt]=1; if(pr[tt]) addbian(pr[tt],tt,mn[tt]), addbian(tt,pr[tt],mn[tt]); for(j=1;j<=all;++j) if(!ff[j]) { vv=dis(tt,j); if(mn[j]>vv) mn[j]=vv,pr[j]=tt; } } dfs(K+1,-1,0); return sqrt(ans)/2.0; } int main(){ //freopen("T1.in","r",stdin); //freopen("T1.out","w",stdout); //freopen("in.in","r",stdin); mem(first,-1); rint i,j; read(n); read(m); read(K); for(i=1;i<=K;++i) readll(x[i]),readll(y[i]); printf("%.8lf",work()); }
T2
首先你可以暴搜(虽然我暴搜也打挂了),有20分
然后你发现,题目其实是让求一个极长的上升序列,可以$O(n^2)$dp,有40分
然后你又发现可以有一个加calc的$O(nlog^2n)$的线段树优化
这个calc的写法其实跟以前 金坷垃、treap、楼房建设 差不多
一般是 维护三个域
ans、由于两个子区间不可加性的辅助转移的变量、记录状态防止超时的量
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <cmath> #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define dd double #define rint register int using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int N=200006; struct son { int pos,p,c; bool friend operator < (son a,son b) { return a.p<b.p; } }ji[N]; int n,INF; int an[N*4],mx[N*4],ban[N*4]; int cal(int hi,int l,int r,int x) { if(hi>mx[x]) return INF; if(l==r) return an[x]; int mid=(l+r)>>1; if(hi>mx[x<<1|1]) return cal(hi,l,mid,x<<1); else return min(ban[x<<1],cal(hi,mid+1,r,x<<1|1)); } void change(int pos,int hh,int vv,int l,int r,int x) { if(l==r) { an[x]=vv; mx[x]=hh; return ; } int mid=(l+r)>>1; if(pos<=mid) change(pos,hh,vv,l,mid,x<<1); else change(pos,hh,vv,mid+1,r,x<<1|1); mx[x]=max(mx[x<<1],mx[x<<1|1]); an[x]=min(an[x<<1|1],ban[x<<1]=cal(mx[x<<1|1],l,mid,x<<1)); } void qq(int L,int R,int &ans,int &hi,int l,int r,int x) { //printf("L=%d R=%d ans=%d hi=%d l=%d r=%d x=%d\n",L,R,ans,hi,l,r,x); if(L>R) return ; if(L<=l&&r<=R) { ans=min(ans,cal(hi,l,r,x)); hi=max(hi,mx[x]); return ; } int mid=(l+r)>>1; if(mid<R) qq(L,R,ans,hi,mid+1,r,x<<1|1); if(L<=mid) qq(L,R,ans,hi,l,mid,x<<1); } int f[N]; int work() { rint i,j; int tt,hi; sort(ji+1,ji+1+n); mem(an,0x7f); mem(ban,0x7f); mem(&INF,0x7f); for(i=1;i<=n;++i) { //printf("i=%d\n",i); hi=0; tt=INF; qq(1,ji[i].pos-1,tt,hi,1,n,1); if(tt==INF) tt=0; f[ji[i].pos]=tt+ji[i].c; change(ji[i].pos,ji[i].p,f[ji[i].pos],1,n,1); } /*printf("\n"); for(i=1;i<=n;++i) printf("%d ",f[i]); printf("\n");*/ return f[n]; } int main(){ //freopen("T2.in","r",stdin); //freopen("T2.out","w",stdout); rint i,j; read(n); for(i=1;i<=n;++i) ji[i].pos=i,read(ji[i].p); for(i=1;i<=n;++i) read(ji[i].c); ++n; ji[n].pos=n; ji[n].c=0; ji[n].p=0x7fffffff; cout<<work(); }
T3
可以把式子变成一个类似于斜率的式子,之后就转变成求一个下凸壳
用可持久化单调栈维护
可持久化?
其实就是一个回溯的过程
弹栈还要二分弹,暴力弹会被卡成$O(n^2)$
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> #include <cmath> #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define dd double #define rint register int using namespace std; inline void read(int &x) { x=0; char q=getchar(); while(q<'0'||q>'9') q=getchar(); while(q>='0'&&q<='9') x=x*10+q-'0',q=getchar(); } const int N=500006; int first[N*3],nt[N*3],ver[N*3],e; void addbian(int u,int v) { ver[e]=v; nt[e]=first[u]; first[u]=e++; } int n; int c[N],dep[N]; int zhan[N*2],he; dd an[N]; inline dd cross(int q,int w) { return (dd)(c[w]-c[q])/(dd)(dep[w]-dep[q]); } int er(int l,int r,int order) { int mid,tt,ans=r; while(l<=r) { mid=(l+r)>>1; tt=(cross(zhan[mid],order)<cross(zhan[mid-1],order)); if(tt) ans=mid-1,r=mid-1; else l=mid+1; } return ans; } void dfs(int x) { int pr,prv; ++he; prv=zhan[he]; zhan[he]=x; pr=he; for(int i=first[x];i!=-1;i=nt[i]) { dep[ver[i]]=dep[x]+1; if(he>1) he=er(2,he,ver[i]); an[ver[i]]=-cross(zhan[he],ver[i]); dfs(ver[i]); he=pr; } zhan[he]=prv; } int main(){ //freopen("T3.in","r",stdin); rint i,j; mem(first,-1); read(n); int tin; for(i=1;i<=n;++i) read(c[i]); for(i=2;i<=n;++i) { read(tin); addbian(tin,i); } dep[1]=1; dfs(1); for(i=2;i<=n;++i) printf("%.10lf\n",an[i]); }