来自FallDream的博客,未经允许,请勿转载,谢谢。
----------------------------------------------------------------------------------------
我写完A题拍不出错,感觉没问题,交上去全RE,发现我竟然在倍增的时候把0的父亲写成了-1 脑抽爆0了.. 本来可以300的
----------------------------------------------------------------------------------------
A.summing
你有一棵以 你有一棵以 0为根,n个节点的树 n<=500000
定义:点u的等级为点u到根的距离,树的高度h为树上所有点的等级中最大值F(l,k)表示子树中等级为l的点数量不小于k的点的数量
现在给你 现在给你 h+1个常数 a0,a1,a2,…,ah,请你求出 ,请你求出 ,请你求出 $sum_{i=0}^{h}F(i,ai)$
题解:我们发现答案只会在相邻两个点的lca处变化,所以可以把同一个等级的点全部拿出来,然后把它们lca的深度塞进一个pq,每次更新答案就可以了。合并次数最多n次,复杂度nlogn
我脑抽,改了就过了。
#include<iostream> #include<cstdio> #include<queue> #include<vector> #define ll long long #define MN 500000 #define MK 20 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } struct eve{ int dep,x,y; bool operator<(const eve&b)const{return dep<b.dep;} }; priority_queue<eve> q; ll ans=0,now=0; int n,h,fa[MN+5][MK+5],cnt=0,head[MN+5],a[MN+5],s[MN+5],dep[MN+5],size[MN+5]; struct edge{int to,next;}e[MN*2+5]; vector<int> v[MN+5]; void ins(int f,int t){ e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } void dfs(int x,int f) { v[dep[x]].push_back(x);fa[x][0]=f; for(int i=head[x];i;i=e[i].next) if(e[i].to!=f) { dep[e[i].to]=dep[x]+1; dfs(e[i].to,x); } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=dep[x]-dep[y],j=0;i;++j,i>>=1) if(i&1) x=fa[x][j]; if(x==y)return x; for(int i=MK;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } int getfa(int x){return s[x]==x?x:s[x]=getfa(s[x]);} void Merge(int x,int y,int lim) { int f1=getfa(x),f2=getfa(y); now-=(size[f1]>=lim)+(size[f2]>=lim); s[f1]=f2,size[f2]+=size[f1]; now+=(size[f2]>=lim); } int main() { freopen("summing19.in","r",stdin); freopen("summing.out","w",stdout); n=read();h=read();int xx; for(int i=1;i<n;i++) xx=read(),ins(xx,i); for(int i=0;i<=h;i++) a[i]=read(); dfs(0,n); for(int i=1;i<=MK;i++) for(int j=0;j<n;j++) fa[j][i]=fa[fa[j][i-1]][i-1]; for(int i=0;i<=h;++i) { if(a[i]==0){ ans+=n;continue;} now=0;if(a[i]==1) now=v[i].size(),ans+=now;int pre=i; for(int j=0;j<v[i].size();++j) s[j]=j,size[j]=1; for(int j=1;j<v[i].size();++j) q.push((eve){dep[lca(v[i][j],v[i][j-1])],j,j-1}); while(!q.empty()) { eve th=q.top();q.pop(); int d=th.dep;ans+=now*(pre-d-1); Merge(th.x,th.y,a[i]); while(!q.empty()&&q.top().dep==d) { th=q.top();q.pop(); Merge(th.x,th.y,a[i]); } pre=d;ans+=now; } ans+=pre*now; } cout<<ans<<endl; return 0; }
B.三角形规划 (你的三角形已如风中残烛) 这个是上次校内训练赛的题..详见这里的C题
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #define eps 1e-10 #define INF 2000000000 #define T 9851 #define S 0 #define ld long double using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int head[T+5],d[T+5],q[T+5],cnt=1,n,tot,top,c[T+5]; struct edge{ int to,next;ld w; }e[1200000]; bool b[55][55]; void ins(int f,int t,ld w) { e[++cnt]=(edge){t,head[f],w};head[f]=cnt; e[++cnt]=(edge){f,head[t],0};head[t]=cnt; } ld dfs(int x,ld f) { if(x==T)return f; ld used=0; for(int&i=c[x];i;i=e[i].next) if(e[i].w>0&&d[e[i].to]==d[x]+1) { ld w=dfs(e[i].to,min(f-used,e[i].w)); used+=w;e[i].w-=w;e[i^1].w+=w; if(fabs(f-used)<eps) return f; } return used; } bool bfs() { memset(d,0,sizeof(d));int i,j; for(d[q[top=i=0]=S]=1;i<=top;i++) for(int j=c[q[i]]=head[q[i]];j;j=e[j].next) if(e[j].w>0&&!d[e[j].to]) d[q[++top]=e[j].to]=d[q[i]]+1; return d[T]>0; } void solve(int x) { if(!d[x]) { d[x]=1; for(int&i=head[x];i;i=e[i].next) if(e[i].w>1e-6) solve(e[i].to); } } int main() { freopen("triangle.in","r",stdin); freopen("triangle.out","w",stdout); tot=n=read(); for(int i=1;i<=n;i++) ins(S,i,0); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]=read(); for(int i=1;i<n;i++) for(int j=i+1;j<n;j++) for(int k=j+1;k<=n;k++) if(b[i][j]&&b[i][k]&&b[j][k]) { ++tot;ins(i,tot,INF);ins(j,tot,INF); ins(k,tot,INF);ins(tot,T,1); } ld l=0,r=393,mid,sum=0; int i,j;tot-=n; while(l+eps<r) { mid=(l+r)/2;sum=0; for(i=2,j=1;j<=n;j++,i+=2) e[i].w=mid,e[i^1].w=0; for(;i<=cnt;i+=2) e[i].w=1,e[i^1].w=0; while(bfs()) sum+=dfs(S,INF); if((ld)tot<=sum)r=mid; else l=mid; } top=0;memset(d,0,sizeof(d)); solve(S); for(int i=1;i<=n;i++)if(!d[i])q[++top]=i; cout<<top<<endl; for(int i=1;i<=top;i++) printf("%d ",q[i]); return 0; }
C.给定一个n个数的序列ai,求有多少对(i,j)满足ai*aj<=max(ai..aj) n<=100000
题解:枚举最大值是哪一个,往两边二分出它是最大值的范围,在两端较小的那一段枚举,另一端在主席树上查最值就行了。由于我们取较小的范围,所以查询次数最多nlogn次,复杂度$nlog^{2}n$
#include<iostream> #include<cstdio> #include<cmath> #define ll long long #define INF 1000000000 #define MN 100000 #define MK 17 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } ll ans=0; int s[MN+5],n,p[MN+5],rt[MN+5],cnt=0; int f[MN+5][MK+3],f2[MN+5][MK+3]; struct Tree{int l,r,x;}T[MN*35]; int query(int x,int nx,int v) { if(x>nx) return 0;x=rt[x-1];nx=rt[nx]; int l=1,r=INF,mid,ans=0; while(l<r) { mid=l+r>>1; if(v<=mid) { nx=T[nx].l;x=T[x].l; r=mid; } else { ans+=T[T[nx].l].x-T[T[x].l].x; nx=T[nx].r;x=T[x].r;l=mid+1; } } return ans+T[nx].x-T[x].x; } void ins(int x,int nx,int v) { int l=1,r=INF,mid; while(l<r) { mid=l+r>>1; if(v<=mid) { T[nx].r=T[x].r;T[nx].l=++cnt; x=T[x].l;nx=T[nx].l;r=mid; } else { T[nx].l=T[x].l;T[nx].r=++cnt; x=T[x].r;nx=T[nx].r;l=mid+1; } T[nx].x=T[x].x+1; } } int calc(int l,int r) { int len=p[r-l+1]; return max(f[l][len],f2[r][len]); } int get(int l,int r,int ans,int x) { int mid,rr=r; while(l<=r) { mid=l+r>>1; if(calc(mid,r)<x) ans=mid,r=mid-1; else l=mid+1; } return ans; } int get2(int l,int r,int ans,int x) { int mid,lt=l; while(l<=r) { mid=l+r>>1; if(calc(lt,mid)<=x) ans=mid,l=mid+1; else r=mid-1; } return ans; } int main() { freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); n=read(); for(int i=0;i<=MK;i++) for(int j=(1<<i)+1;j<=min(n,1<<(i+1));j++) p[j]=i; for(int i=1;i<=n;i++)s[i]=f[i][0]=f2[i][0]=read(); for(int i=1;i<=n;i++)ins(rt[i-1],rt[i]=++cnt,s[i]); for(int i=1,k=1;i<=MK;i++,k<<=1) for(int j=1;j<=n;j++) f[j][i]=max(f[j][i-1],f[min(j+k,n)][i-1]), f2[j][i]=max(f2[j][i-1],f2[max(1,j-k)][i-1]); for(int i=1;i<=n;i++) { int l=get(1,i-1,i,f[i][0]),r=get2(i+1,n,i,f[i][0]); if(i-l<r-i) for(int j=l;j<=i;j++) ans+=query(max(i,j+1),r,s[i]/s[j]); else for(int j=i;j<=r;j++) ans+=query(l,min(i,j-1),s[i]/s[j]); } cout<<ans<<endl; return 0; }
D.需要你构造一个有n个点,每个点都恰好有m条单向的出边的图,使得点两两之间的最大距离最小。n<=1000,m<=5 当答案与最小直径相差不超过1的时候判对。
题解:我们考虑一个倍增的思想。如果要表示1-7的数,我们会选择1 2 4。同理,我们对于每个点i,连接i和i*m+j (j=0~m-1) ,这样在m^k>=n的时候就能保证任意一个点i在k步之内能走到任意一个点,直接算$m^{x}>=n$的x作为直径就好啦
#include<iostream> #include<cstdio> #include<cstring> #define MN 1000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } int n,m; int main() { freopen("diameter.in","r",stdin); freopen("diameter.out","w",stdout); n=read();m=read(); int ans=0; for(int i=1;i<n;i*=m,ans++); cout<<ans<<endl; for(int i=0;i<n;i++,puts("")) for(int j=0;j<m;j++) printf("%d ",(i*m+j)%n); return 0; }