BZOJ 10.29--11.7刷题总结
在总结之前,预祝2018级信奥队明天NOIP RP++
@JZYshuraK @ysy20021208 @jiangminghong @EM_LGH @zcs0724 以及我自己
开始总结题目
BZOJ1977 [BeiJing2010组队]次小生成树 Tree
BZOJ 1977[BeiJing2010组队]次小生成树 Tree
这道题我们用Kruskal把这棵最小生成树构建出来,之后我们枚举每一条非树边,也需要快速求出每一条树链上的最小边与次小边。
这个东西我们可以用倍增LCA来求。
D[x][i]=max(D[x][i-1],D[f[x][i-1]][i-1]);
D2[x][i]=max(D2[x][i-1],D2[f[x][i-1]][i-1]);
D2[x][i]=min(D[x][i-1],D[f[x][i-1]][i-1]);
D2[x][i]=max(D2[x][i],D2[x][i-1]);
D2[x][i]=max(D2[x][i],D2[f[x][i-1]][i-1]);
这几步就是求最长链与次长链。
之后就没了。
#include<cstdio> #include<algorithm> #define N 300010 #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) using namespace std; int dep[N]; int f[N][20]; int head[N]; int to[N]; int nex[N]; int fa[N]; int val[N]; int idx; int n,m; int D[N][20]; int D2[N][20]; int size[N]; long long ans; int minn=0x3f3f3f3f; struct tree { int x,y,z,is; }a[N]; bool cmp(const tree &a,const tree &b){return a.z<b.z;} void addedge(int a,int b,int c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } int find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } void dfs(int x) { for(int i=1;i<=17;i++) { if(dep[x]<(1<<i)) break; f[x][i]=f[f[x][i-1]][i-1]; D[x][i]=max(D[x][i-1],D[f[x][i-1]][i-1]); if(D[x][i-1]==D[f[x][i-1]][i-1]) D2[x][i]=max(D2[x][i-1],D2[f[x][i-1]][i-1]); else { D2[x][i]=min(D[x][i-1],D[f[x][i-1]][i-1]); D2[x][i]=max(D2[x][i],D2[x][i-1]); D2[x][i]=max(D2[x][i],D2[f[x][i-1]][i-1]); } } for(int i=head[x];i;i=nex[i]) { if(to[i]!=f[x][0]) { dep[to[i]]=dep[x]+1; D[to[i]][0]=val[i]; f[to[i]][0]=x; dfs(to[i]); } } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int deep=dep[x]-dep[y]; for(int i=17;i>=0;i--) if(deep&(1<<i)) x=f[x][i]; for(int i=17;i>=0;i--) { if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; } if(x==y) return x; return f[x][0]; } void merge(int x,int y) { if(size[x]>size[y]) { fa[y]=x; size[x]+=size[y]; } else { fa[x]=y; size[y]+=size[x]; } } void go(int x,int fa,int cost) { int max1=0,max2=0; int deep=dep[x]-dep[fa]; for(int i=0;i<=17;i++) { if(deep&(1<<i)) { if(D[x][i]>max1) { max2=max1; max1=D[x][i]; } max2=max(max2,D2[x][i]); x=f[x][i]; } } if(max1!=cost) minn=min(minn,cost-max1); else minn=min(minn,cost-max2); } void solve(int j,int cost) { int x=a[j].x; int y=a[j].y; int fa=lca(x,y); go(x,fa,cost); go(y,fa,cost); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); sort(a+1,a+m+1,cmp); int tot=0; for(int i=1;i<=m;i++) { int fx=find(a[i].x); int fy=find(a[i].y); if(fx!=fy) { tot++; a[i].is=1; ans+=a[i].z; merge(fx,fy); addedge(a[i].x,a[i].y,a[i].z); addedge(a[i].y,a[i].x,a[i].z); if(tot==n-1) break; } } dfs(1); for(int i=1;i<=m;i++) if(!a[i].is) solve(i,a[i].z); printf("%lld",ans+minn); }
2005: [Noi2010]能量采集
∑x=1n∑y=1m2×((x,y)−1)+1∑x=1n∑y=1m2×((x,y)−1)+1
之后可得−n×m+2∑x=1n∑y=1m(x,y)−n×m+2∑x=1n∑y=1m(x,y)
之后我们直接求解。
#include<stdio.h> long long min(long long x,long long y) { if(x>y) return y; return x; } long long n,m; long long f[100001]; int main() { scanf("%lld%lld",&n,&m); long long ans2=min(n,m),ans=0; for(long long i=ans2;i>=1;i--) { f[i]=(m/i)*(n/i); for(long long j=2*i;j<=ans2;j+=i) f[i]-=f[j]; ans+=f[i]*(2*i-1); } printf("%lld",ans); }
2007: [Noi2010]海拔
和狼抓兔子有点像,每一点的高度只能为0或1.平面图转对偶图跑最短路。
#include<cstdio> #include<queue> #include<algorithm> #include<cstring> using namespace std; #define N 1500002 int S=0,T=1500001; int idx; int head[N]; int to[N]; int val[N]; int nex[N]; long long f[N]; int inq[N]; int x; int n; void addedge(int a,int b,int c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } int arr(int x,int y) { if(x==0||y>n) return S; if(x>n||y==0) return T; return (x-1)*n+y; } struct Point { int dis,number; inline bool operator < (const Point &a) const { return dis>a.dis; } }; priority_queue < Point > q; void dijkstra(int S) { Point tmp; tmp.number=S; tmp.dis=0; for(int i=1;i<N;i++) f[i]=1e16; f[tmp.number]=0; q.push(tmp); while(!q.empty()) { int x=q.top().number; q.pop(); if(inq[x]) continue; inq[x]=0; for(int i=head[x];i;i=nex[i]) { if(f[to[i]]>f[x]+val[i]&&(!inq[to[i]])) { f[to[i]]=f[x]+val[i]; tmp.dis=f[to[i]]; tmp.number=to[i]; q.push(tmp); } } } } int main() { scanf("%d",&n); for(int i=1;i<=n+1;i++) for(int j=1;j<=n;j++) { scanf("%d",&x); addedge(arr(i-1,j),arr(i,j),x); } for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++) { scanf("%d",&x); addedge(arr(i,j),arr(i,j-1),x); } for(int i=1;i<=n+1;i++) for(int j=1;j<=n;j++) { scanf("%d",&x); addedge(arr(i,j),arr(i-1,j),x); } for(int i=1;i<=n;i++) for(int j=1;j<=n+1;j++) { scanf("%d",&x); addedge(arr(i,j-1),arr(i,j),x); } dijkstra(S); printf("%lld",f[T]); return 0; }
1079: [SCOI2008]着色方案
guangheli的考试题,10分部分分,我直接爆蛋。
K和Ci都比较小,我们考虑用记忆化搜索。
Ci为5,所以我们可以把它存进一个桶里,之后6维DP
f[a][b][c][d][e][last]表示每种颜色的个数以及最后一个数的颜色。
#include<cstdio> long long f[16][16][16][16][16][6]; long long t[16]; long long mod=1e9+7; int n,x; long long dfs(long long a,long long b,long long c,long long d,long long e,long long last) { if(f[a][b][c][d][e][last]!=0) return f[a][b][c][d][e][last]; if(a+b+c+d+e==0) return 1; long long ans=0; if(a!=0) ans+=(a-(last==2))*dfs(a-1,b,c,d,e,1); if(b!=0) ans+=(b-(last==3))*dfs(a+1,b-1,c,d,e,2); if(c!=0) ans+=(c-(last==4))*dfs(a,b+1,c-1,d,e,3); if(d!=0) ans+=(d-(last==5))*dfs(a,b,c+1,d-1,e,4); if(e!=0) ans+=e*dfs(a,b,c,d+1,e-1,5); f[a][b][c][d][e][last]=ans%mod; return f[a][b][c][d][e][last]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); t[x]++; } printf("%lld",dfs(t[1],t[2],t[3],t[4],t[5],0)); }
BZOJ 4326: NOIP2015 运输计划
把一条边置成0,问最长链的最小值。
我们可以用倍增LCA构建这棵树,之后差分数组+二分答案解决此题。
我校OJ卡常,我也没喆
#include<cstdio> #include<algorithm> using namespace std; #define N 300010 int fa[N][20]; int head[N]; int to[N*2]; int val[N*2]; int nex[N*2]; int n,m; long long dis[N]; long long length[N]; int lu[N],lv[N]; long long sum; int a,b,c; int lca[N]; long long mid; int f[N]; int dep[N]; int idx; void addedge(int a,int b,int c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } void dfs(int x,int f) { dep[x]=dep[f]+1; fa[x][0]=f; for(int i=head[x];i;i=nex[i]) if(to[i]!=f) { dis[to[i]]=dis[x]+val[i]; dfs(to[i],x); } } int Lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int deep=dep[x]-dep[y]; for(int i=19;i>=0;i--) if(deep&(1<<i)) x=fa[x][i]; for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; if(x==y) return x; return fa[x][0]; } void dfs2(int x,int fa) { for(int i=head[x];i;i=nex[i]) if(to[i]!=fa) { dfs2(to[i],x); f[x]+=f[to[i]]; } } bool check(int x) { int many=0; for(int i=1;i<=n;i++) f[i]=0; for(int i=1;i<=m;i++) if(length[i]>mid) { many++; f[lu[i]]++; f[lv[i]]++; f[lca[i]]-=2; } dfs2(1,0); for(int i=2;i<=n;i++) if(f[i]>=many&&sum-dis[i]+dis[fa[i][0]]<=mid) return 1; return 0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); addedge(b,a,c); addedge(a,b,c); } dfs(1,0); for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; for(int i=1;i<=m;i++) { scanf("%d%d",&lu[i],&lv[i]); lca[i]=Lca(lu[i],lv[i]); length[i]=dis[lu[i]]+dis[lv[i]]-2*dis[lca[i]]; sum=max(sum,length[i]); } long long l=0,r=sum+1; while(l<r) { mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid+1; } printf("%lld",l); }
2190: [SDOI2008]仪仗队
大水题,线筛欧拉函数不要写错哦!
#include<cstdio> #define N 500010 int n; int phi[N]; bool notprime[N]; int prime[N]; int idx; long long ans; int main() { scanf("%d",&n); for(int i=2;i<=n;i++) { if(!notprime[i]) prime[++idx]=i,phi[i]=i-1; for(int j=1;j<=idx&&i*prime[j]<=n;j++) { notprime[i*prime[j]]=1; if(i%prime[j]==0) { phi[i*prime[j]]=phi[i]*prime[j]; break; } else phi[i*prime[j]]=phi[i]*(prime[j]-1); } } for(int i=2;i<n;i++) ans+=phi[i]; if(n==1) { printf("0"); return 0; } printf("%lld",ans*2+3); }
1040: [ZJOI2008]骑士
N个点,N条边显然是个基环树。我们要拆掉所有环之后进行DP
f[i]表示选择i点及子树最大值
g[i]表示不选择i点及子树最大值
最后的答案就是那些环中点g[i]的最大值
#include<cstdio> #define max(a,b) ((a) > (b) ? (a) : (b)) #define N 2000100 int n; long long fight[N]; int fa[N]; int a; int head[N]; int to[N]; int nex[N]; int idx; int root1,root2; long long f[N]; long long g[N]; long long ans,ans2; int lu[N]; int lv[N]; int tot; void addedge(int a,int b) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; } int find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } void dfs(int x,int fa) { f[x]=fight[x]; g[x]=0; for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa) { dfs(to[i],x); g[x]+=max(f[to[i]],g[to[i]]); f[x]+=g[to[i]]; } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) { scanf("%lld%d",&fight[i],&a); int fx=find(i); int fy=find(a); if(fx!=fy) { addedge(i,a); addedge(a,i); fa[fx]=fy; } else { lu[++tot]=a; lv[tot]=i; } } for(int i=1;i<=tot;i++) { dfs(lu[i],0); ans2=g[lu[i]]; dfs(lv[i],0); ans2=max(g[lv[i]],ans2); ans+=ans2; } printf("%lld",ans); }
BZOJ 2173 整数的lqp拆分
jiangminghong的考试题,好题就被我手动打表找规律废掉了
ans[i]=*ans[i-1]+ans[i-2]
#include<cstdio> #include<algorithm> using namespace std; #define N 3000100 int mod=1e9+7; int n; long long ans[N]; int main() { scanf("%d",&n); ans[1]=1,ans[2]=2,ans[3]=5; for(int i=4;i<=n;i++) ans[i]=((ans[i-1]*2)%mod+ans[i-2])%mod; printf("%lld",ans[n]); return 0; }
BZOJ 1131 [POI2008]Sta
我又想起了llq说的扭一扭,这道题就是这么更新的。
我们先DFS扫一遍,之后对于每个点将其扭到根,它子树的dep都减去1,其他点的dep都加上1.
之后我们O(n)解决!
#include<cstdio> #include<algorithm> using namespace std; #define N 2000010 int n; int nex[N]; int to[N]; int dep[N]; int size[N]; int head[N]; long long ans[N]; long long ans2; int idx; int a,b,ans3; void addedge(int a,int b) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; } void dfs(int x,int fa) { size[x]=1; dep[x]=dep[fa]+1; for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa) { dfs(to[i],x); size[x]+=size[to[i]]; } } } void pose(int x,int fa) { ans[x]=ans[fa]-size[x]+(n-size[x]); for(int i=head[x];i;i=nex[i]) if(to[i]!=fa) pose(to[i],x); } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); addedge(a,b); addedge(b,a); } dfs(1,0); for(int i=1;i<=n;i++) ans[1]+=dep[i]; pose(1,1); for(int i=1;i<=n;i++) if(ans2<ans[i]) { ans2=ans[i]; ans3=i; } printf("%d",ans3); }
2208: [Jsoi2010]连通数
JZYshuraK的好想法,刚开始我一眼tarjan,JZYshuraK告诉我直接floyd让我直接蒙圈。
他说闭包传递+bitset优化可解决。2*108过掉,码量超短。
#include <cstdio> #include<bitset> #include<algorithm> using namespace std; bitset<2010> is[2010]; char s[2010][2010]; int ans=0; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(s[i][j]=='1'||i==j) is[i][j]=true; for(int j=1;j<=n;j++) for(int i=1;i<=n;i++) if(is[i][j]) is[i]|=is[j]; for(int i=1;i<=n;i++) ans+=is[i].count(); printf("%d",ans); return 0; }
1060: [ZJOI2007]时态同步
f[i]表示距离i最远点的距离,之后直接裸树形DP
#include<cstdio> #include<algorithm> using namespace std; #define N 1000001 int n,S; int head[N]; int to[N]; int nex[N]; long long val[N]; long long f[N]; int a,b; long long c; int idx; long long ans; void addedge(int a,int b,long long c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } void dfs(int x,int fa) { for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa) { dfs(to[i],x); f[x]=max(f[to[i]]+val[i],f[x]); } } for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa) { ans=ans+f[x]-val[i]-f[to[i]]; } } } int main() { scanf("%d",&n); scanf("%d",&S); for(int i=1;i<n;i++) { scanf("%d%d%lld",&a,&b,&c); addedge(a,b,c); addedge(b,a,c); } dfs(S,0); printf("%lld",ans); return 0; }
BZOJ 1077 天平
floyd跑差分约束,我刚开始还写WA了。
#include<cstdio> #include<algorithm> using namespace std; #define N 501 int n,A,B; char s[101][101]; int mx[501][501]; int mn[501][501]; int c1,c2,c3; int main() { scanf("%d%d%d",&n,&A,&B); for(int i=1;i<=n;i++) scanf("%s",s[i]+1); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(s[i][j]=='+') { mn[i][j]=1; mx[i][j]=2; } else if(s[i][j]=='-') { mn[i][j]=-2; mx[i][j]=-1; } else if(s[i][j]=='?') { mn[i][j]=-2; mx[i][j]=2; } else { mn[i][j]=0; mx[i][j]=0; } } mn[i][i]=0; mx[i][i]=0; } for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i==j||i==k||j==k) continue; mn[i][j]=max(mn[i][j],mn[i][k]+mn[k][j]); mx[i][j]=min(mx[i][j],mx[i][k]+mx[k][j]); } for(int i=1;i<=n;i++) { if(i==A||i==B) continue; for(int j=i+1;j<=n;j++) { if(j==A||j==B) continue; if(mn[A][i]>mx[j][B]||mn[A][j]>mx[i][B]) c1++; if(mx[A][i]<mn[j][B]||mx[A][j]<mn[i][B]) c3++; if((mn[A][i]==mx[A][i]&&mn[B][j]==mx[B][j]&&mn[A][i]+mx[B][j]==0)||(mn[A][j]==mx[A][j]&&mn[B][i]==mx[B][i]&&mn[A][j]+mx[B][i]==0)) c2++; } } printf("%d %d %d",c1,c2,c3); return 0; }
BZOJ 5165 树上倍增
听ysy20021208说是JLOI 2018 D2 T2佛光树改了一个数据范围
这题直接新建节点跑LCA就OK了
#include<cstdio> #include<algorithm> using namespace std; #define N 3000001 int n,m,K; int f[N][21]; int dep[N]; char opt[2]; int idx=1; int x,y; int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int deep=dep[x]-dep[y]; for(int i=20;i>=0;i--) if(deep&(1<<i)) x=f[x][i]; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; if(x==y) return x; return f[x][0]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%s",opt); if(opt[0]=='A') { scanf("%d",&x); dep[++idx]=dep[x]+1; f[idx][0]=x; for(int i=1;i<=20;i++) { if(!f[idx][i-1]) break; f[idx][i]=f[f[idx][i-1]][i-1]; } } if(opt[0]=='Q') { scanf("%d",&K); K--; scanf("%d",&x); while(K--) { scanf("%d",&y); if(x!=1) x=lca(x,y); } printf("%d\n",x); } } }
BZOJ 2705 Longge的问题
欧拉函数好题
从老师PPT直接摘下
#include<cstdio> typedef long long ll; ll n; ll ans; ll oula(ll n) { ll ans=n; for(int i=2;i*i<=n;i++) { if(n%i==0) { ans=ans/i*(i-1); while(n%i==0) n/=i; } } if(n>1) ans=ans/n*(n-1); return ans; } int main() { scanf("%lld",&n); for(int i=1;i*i<=n;i++) if(n%i==0) { ans+=oula(n/i)*i; if(i*i<n) ans+=(n/i)*oula(i); } printf("%lld",ans); return 0; }
BZOJ 4800 [Ceoi2015]Ice Hockey World Championship
n<=40,一眼 Meet in the middle,分成两个序列之后二分
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; ll n; ll m; ll a[1<<22]; ll b[1<<22]; ll idx1,idx2; ll val[44]; ll ans; void dfs(ll dep,ll n,ll m,ll sum) { if(sum>m) return ; if(dep>n) { a[++idx1]=sum; return ; } dfs(dep+1,n,m,sum); dfs(dep+1,n,m,sum+val[dep]); } void dfs2(ll dep,ll n,ll m,ll sum) { if(sum>m) return ; if(dep>n) { b[++idx2]=sum; return ; } dfs2(dep+1,n,m,sum); dfs2(dep+1,n,m,sum+val[dep]); } int main() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++) scanf("%lld",&val[i]); dfs(1,n>>1,m,0); dfs2((n>>1)+1,n,m,0); sort(a+1,a+idx1+1); sort(b+1,b+idx2+1); for(ll i=1;i<=idx2;i++) { if(m-b[i]>=a[idx1]) ans+=idx1; else ans+=upper_bound(a+1,a+idx1+1,m-b[i])-a-1; } printf("%lld",ans); }
BZOJ 1430 小猴打架
通过这道题我学会了一个新知识点--Prufer序列
显然,对于N个节点的生成树,种类有N^(N-2),因为这道题对与打架顺序还要重复计算,所以再来个N!/N,也即是(N-1)!,所以F(x)=(x-1)!*x^(x-2)
#include<cstdio> typedef long long ll; ll mod=9999991; ll n,ans=1; int main() { scanf("%lld",&n); for(int i=1;i<=n-2;i++) ans=(ans*n)%mod; for(int i=2;i<n;i++) ans=(ans*i)%mod; printf("%lld",ans); }
BZOJ 4173 数学
结果是phi(n)*phi(m)*n*m
#include<cstdio> typedef long long ll; ll mod=998244353; ll n,m; ll ans; ll phi(ll n) { ll ans=n; for(ll i=2;i*i<=n;i++) { if(n%i==0) { ans=ans/i*(i-1); while(n%i==0) n/=i; } } if(n!=1) ans=ans/n*(n-1); return ans%mod; } int main() { scanf("%lld%lld",&n,&m); ans=phi(n)*phi(m)%mod*(n%mod)%mod*(m%mod)%mod; printf("%lld",ans); return 0; }
BZOJ 3517 翻硬币
好题一道,刚开始一眼高斯消元解异或方程组。后来才看出来
设is[i][j]为此状态翻不翻。决定这个点只有这一行与这一列的情况,我们求一个前缀异或值,倒过来就是1的状态
#include<cstdio> int n; int a[1001][1001]; int is[1001][1001]; int row[1001]; int column[1001]; int ans; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%1d",&a[i][j]); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { row[i]^=a[i][j]; column[j]^=a[i][j]; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int tmp=row[i]^column[j]; tmp^=a[i][j]; ans+=tmp; } if(ans>n*n-ans) ans=n*n-ans; printf("%d",ans); }
BZOJ 2438: [中山市选2011]杀人游戏
tarjan缩点+判断,有坑。最后一个缩块入度为0并且只有一个点答案-1
#include<cstdio> #include<algorithm> using namespace std; #define N 300010 int tot; int nex[N<<1]; int to[N<<1]; int head[N<<1]; int dep[N]; int low[N]; int blob[N]; int stack[N]; int vis[N]; int inz[N]; int top; int cnt,idx; int sum; int in[N]; int inblob[N]; int n,m; int x,y; int ans; void addedge(int a,int b) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; } void tarjan(int x) { dep[x]=low[x]=++cnt; stack[++top]=x; vis[x]=inz[x]=1; for(int i=head[x];i;i=nex[i]) { if(!vis[to[i]]) { tarjan(to[i]); low[x]=min(low[x],low[to[i]]); } else if(inz[to[i]]) low[x]=min(low[x],dep[to[i]]); } if(dep[x]==low[x]) { int here; sum++; do { here=stack[top--]; blob[here]=sum; inz[here]=0; in[sum]++; }while(here!=x); } } bool is(int x) { int i; bool flag=1; for(i=head[x];i;i=nex[i]) inblob[blob[to[i]]]--; for(i=head[x];i;i=nex[i]) if(!inblob[blob[to[i]]]) flag=0; for(i=head[x];i;i=nex[i]) inblob[blob[to[i]]]++; return flag; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); addedge(x,y); } for(int i=1;i<=n;i++) if(!vis[i]) tarjan(i); for(int x=1;x<=n;x++) for(int i=head[x];i;i=nex[i]) if(blob[x]!=blob[to[i]]) inblob[blob[to[i]]]++; for(int i=1;i<=sum;i++) if(!inblob[i]) ans++; int i; for(i=1;i<=n;i++) if(in[blob[i]]==1&&!inblob[blob[i]]&&is(i)) break; if(i<=n) ans--; printf("%.6lf\n",(double)(n-ans)/n); return 0; }
BZOJ 5085 最大
这题思维难度较大,二分+枚举线段
总时间O(n2logmaxlongint)
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; int boom[1001][1001]; int a[1001][1001]; int tot,cnt[1001]; int n,m; int l=0x7f7f7f7f,r=0,mid; int ans; bool check(int mid) { memset(boom,0,sizeof(boom)); for(int i=1;i<=n;i++) { tot=0; for(int j=1;j<=m;j++) if(a[i][j]>=mid) cnt[++tot]=j; for(int j=1;j<=tot;j++) for(int k=j+1;k<=tot;k++) { if(boom[cnt[j]][cnt[k]]==1) return 1; boom[cnt[j]][cnt[k]]=1; } } return 0; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&a[i][j]); l=min(l,a[i][j]); r=max(r,a[i][j]); } while(l<r) { int mid=(l+r)>>1; if(check(mid)) l=mid+1,ans=mid; else r=mid; } printf("%d",ans); return 0; }
BZOJ 1954 The Xor-longest Path
Trie树题目,dfs边权时变成异或
#include<cstdio> #include<algorithm> using namespace std; #define N 1500010 int n; int a,b,c; int head[N]; int to[2*N]; int val[2*N]; int nex[2*N]; int ch[N][2]; int dis[N]; int cnt; int ans; int idx; void addedge(int a,int b,int c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } void trie(int x) { int here=0; bool is; for(int i=30;i>=0;i--) { if(x&(1<<i)) is=1; else is=0; if(!ch[here][is]) ch[here][is]=++cnt; here=ch[here][is]; } } void dfs(int x,int fa) { for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa) { dis[to[i]]=dis[x]^val[i]; dfs(to[i],x); } } } int find(int x) { int here=0,ans=0; for(int i=30;i>=0;i--) { int is=x&(1<<i)?1:0; if(ch[here][!is]) ans|=(1<<i),here=ch[here][!is]; else here=ch[here][is]; } return ans; } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d%d",&a,&b,&c); addedge(a,b,c); addedge(b,a,c); } dfs(1,0); for(int i=1;i<=n;i++) { trie(dis[i]); ans=max(ans,find(dis[i])); } printf("%d",ans); }
BZOJ 3251树上三角形
总共fib数列也没几个数,直接暴力LCA更新就OK
#include<cstdio> #include<algorithm> using namespace std; #define N 1000100 int n,Q; int val[N]; int fa[N]; int a,b; int head[N]; int to[N]; int nex[N]; int dep[N]; int idx=1; int ans[N]; int tot; void addedge(int a,int b) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; } void dfs(int x) { for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa[x]) { dep[to[i]]=dep[x]+1; dfs(to[i]); } } } bool go(int x,int y) { if(dep[x]<dep[y]) swap(x,y); tot=0; while(dep[x]>dep[y]) { ans[++tot]=val[x]; x=fa[x]; if(tot>50) return 1; } while(x!=y) { ans[++tot]=val[x]; ans[++tot]=val[y]; x=fa[x]; y=fa[y]; if(tot>50) return 1; } ans[++tot]=val[x]; sort(ans+1,ans+tot+1); for(int i=3;i<=tot;i++) { if(ans[i]-ans[i-2]<ans[i-1]) return 1; } return 0; } int main() { scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++) scanf("%d",&val[i]); for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); addedge(a,b); fa[b]=a; } dfs(1); while(Q--) { int opt; scanf("%d",&opt); if(opt==0) { scanf("%d%d",&a,&b); if(go(a,b)) puts("Y"); else puts("N"); } else { scanf("%d%d",&a,&b); val[a]=b; } } }
BZOJ 2523 聪明的学生
强大的题目,贼神奇,其实是个迭代的过程
#include<cstdio> using namespace std; int n,m,last[3]={2,0,1},nex[3]={1,2,0},ans[30005][3],Ans,num; void work(int x,int y,int t) { if(Ans>n) return; if(x==y) { Ans+=t+1; return; } if(x>y) { Ans+=2; work(y,x-y,nex[t]); } else { Ans++; work(y-x,x,last[t]); } } int main() { while(scanf("%d%d",&n,&m)&&n!=-1) { num=0; for(int i=1;i<m;i++) { int j=m-i; Ans=0; work(i,j,(n-1)%3); if(Ans==n) ans[++num][nex[(n-1)%3]]=i,ans[num][(n-1)%3]=m,ans[num][last[(n-1)%3]]=j; } printf("%d\n",num); if((n-1)%3==1) for(int i=num;i>0;i--) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); else for(int i=1;i<=num;i++) printf("%d %d %d\n",ans[i][0],ans[i][1],ans[i][2]); } return 0; }
BZOJ 4668 冷战
并查集路径压缩+启发式合并&LCA
#include<cstdio> #include<algorithm> using namespace std; #define N 1000100 int n,m; int ans[N]; int fa[N]; int size[N]; int idx; int lastans; int ti[N]; int dep[N]; int tim=0; int x,y; int find(int x) { int f; if(fa[x]==x) return fa[x]; else { f=find(fa[x]); dep[x]=dep[fa[x]]+1; } return f; } void merge(int x,int y,int i) { int fx=find(x); int fy=find(y); if(fx!=fy) { if(size[fx]>size[fy]) swap(fx,fy); fa[fx]=fy; size[fy]+=fx; ti[fx]=i; } } int lca(int x,int y) { int fx=find(x); int fy=find(y); if(fx!=fy) return 0; int ans=0; if(dep[x]<dep[y]) swap(x,y); while(dep[x]>dep[y]) { ans=max(ans,ti[x]); x=fa[x]; } while(x!=y) { ans=max(ans,ti[x]); ans=max(ans,ti[y]); x=fa[x]; y=fa[y]; } return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fa[i]=i,size[i]=1; int opt; for(int i=1;i<=m;i++) { scanf("%d",&opt); if(!opt) { scanf("%d%d",&x,&y); x=lastans^x; y=lastans^y; merge(x,y,++tim); } else { scanf("%d%d",&x,&y); x=lastans^x; y=lastans^y; int ans=lca(x,y); printf("%d\n",ans); lastans=ans; } } return 0; }
BZOJ 3252: 攻略
ysy20021208的考试题。
我们可以长链剖分直接解决这个问题,取K条最长链
#include<cstdio> #include<algorithm> using namespace std; #define N 500010 int to[N]; int head[N]; int nex[N]; int son[N]; long long dep[N]; int a[N]; int fa[N]; long long mx[N]; int n,K; int x,y; int idx; long long lian[N]; int que[N]; int cnt; long long ans; bool cmp(int x,int y) { return lian[x]>lian[y]; } void addedge(int a,int b) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; } void dfs(int x,int f) { dep[x]=dep[f]+a[x]; fa[x]=f; mx[x]=dep[x]; for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa[x]) { dfs(to[i],x); mx[x]=max(mx[x],mx[to[i]]); if(mx[to[i]]>mx[son[x]]) son[x]=to[i]; } } } void dfs2(int x,int t) { lian[t]+=a[x]; if(son[x]) dfs2(son[x],t); for(int i=head[x];i;i=nex[i]) { if(to[i]!=fa[x]&&to[i]!=son[x]) { que[++cnt]=to[i]; dfs2(to[i],to[i]); } } } int main() { scanf("%d%d",&n,&K); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); addedge(x,y); addedge(y,x); } dfs(1,0); que[++cnt]=1; dfs2(1,1); sort(que+1,que+cnt+1,cmp); for(int i=1;i<=K;i++) ans+=lian[que[i]]; printf("%lld",ans); }
BZOJ 1452 JSOI2009 Count
二维树状数组直接解决修改&查询问题
#include<cstdio> int f[301][301][101]; int map[301][301]; int n,m; int x,y,c; int x1,x2,y1,y2; int lowbit(int x){return x&-x;} void update(int x,int y,int val,int delta) { for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=m;j+=lowbit(j)) f[i][j][val]+=delta; } int query(int x,int y,int val) { int ans=0; for(int i=x;i;i-=lowbit(i)) for(int j=y;j;j-=lowbit(j)) ans+=f[i][j][val]; return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { scanf("%d",&map[i][j]); update(i,j,map[i][j],1); } int Q,opt; scanf("%d",&Q); while(Q--) { int ans=0; scanf("%d",&opt); if(opt==1) { scanf("%d%d%d",&x,&y,&c); update(x,y,map[x][y],-1); map[x][y]=c; update(x,y,c,1); } else { scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&c); ans=ans+query(x2,y2,c)-query(x1-1,y2,c)-query(x2,y1-1,c)+query(x1-1,y1-1,c); printf("%d\n",ans); } } }
BZOJ 1485 有趣的数列
和括号序一样的Catlan数。
之后不让直接求md
#include<cstdio> #define N 2000001 long long n,P; long long prime[N]; bool notprime[N]; int idx; long long pos[N]; long long f[N]; long long ans=1; void change(int x,int delta) { while(x!=1) { f[prime[pos[x]]]+=delta; x/=prime[pos[x]]; } } long long pow(long long x,long long y) { long long ans=1; while(y) { if(y&1) ans=(ans*x)%P; x=(x*x)%P; y>>=1; } return ans; } int main() { scanf("%lld%lld",&n,&P); for(int i=2;i<=2*n;i++) { if(!notprime[i]) prime[++idx]=i,pos[i]=idx; for(int j=1;j<=idx&&i*prime[j]<=2*n;j++) { notprime[i*prime[j]]=1; pos[i*prime[j]]=j; if(i%prime[j]==0) break; } } for(int i=n+2;i<=2*n;i++) change(i,1); for(int i=1;i<=n;i++) change(i,-1); for(int i=1;i<=idx;i++) if(f[prime[i]]) ans=ans*pow(prime[i],f[prime[i]])%P; printf("%lld",ans%P); }
BZOJ 2259新型计算机
刚开始就想最短路了,但是只拿了50分暴力
100分的建图非常神奇了
#include<cstdio> #include<algorithm> #include<cmath> #include<queue> #include<cstring> using namespace std; #define N 4000100 int n; int x; int head[N]; int to[N]; int val[N]; int nex[N]; int f[N]; int vis[N]; int vis2[N]; int idx; void addedge(int a,int b,int c) { nex[++idx]=head[a]; head[a]=idx; to[idx]=b; val[idx]=c; } struct Point { int number,dis; inline bool operator < (const Point &a)const { return dis>a.dis; } }; priority_queue <Point> q; void dijkstra(int S) { memset(f,0x3f,sizeof(f)); Point tmp; tmp.number=S; tmp.dis=0; f[S]=0; q.push(tmp); while(!q.empty()) { int x=q.top().number; q.pop(); for(int i=head[x];i;i=nex[i]) { if(f[to[i]]>f[x]+val[i]) { f[to[i]]=f[x]+val[i]; tmp.dis=f[to[i]]; tmp.number=to[i]; q.push(tmp); } } } } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&x); for(int j=i+1;j<=min(i+x+1,n)&&(!vis[j]);j++) vis[j]=1,addedge(j,j-1,1); for(int j=i+x+1;j<=n&&!vis2[j];j++) vis2[j]=1,addedge(j,j+1,1); if(i+x+1<=n+1) addedge(i,i+x+1,0); else addedge(i,n+1,i+x-n); } dijkstra(1); printf("%d",f[n+1]); return 0; }
BZOJ 1047 理想的正方形
二维RMQ解决,并不是单调队列
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; #define N 1010 int f[11][N][N],f2[11][N][N]; int ans=2e9; int map[N][N]; int minn,maxn; int a,b,n; int k; int main() { scanf("%d%d%d",&a,&b,&n); for(int i=1;i<=a;i++) for(int j=1;j<=b;j++) { scanf("%d",&map[i][j]); f[0][i][j]=f2[0][i][j]=map[i][j]; } for(k=1;(1<<k)<=n;k++) for(int i=1;i<=a-(1<<k)+1;i++) for(int j=1;j<=b-(1<<k)+1;j++) { f[k][i][j]=max(max(f[k-1][i+(1<<(k-1))][j],f[k-1][i][j]),max(f[k-1][i][j+(1<<(k-1))],f[k-1][i+(1<<(k-1))][j+(1<<(k-1))])); f2[k][i][j]=min(min(f2[k-1][i+(1<<(k-1))][j],f2[k-1][i][j]),min(f2[k-1][i][j+(1<<(k-1))],f2[k-1][i+(1<<(k-1))][j+(1<<(k-1))])); } for(int i=1;i<=a-n+1;i++) for(int j=1;j<=b-n+1;j++) { maxn=max(max(f[k-1][i][j],f[k-1][i][j+n-(1<<(k-1))]),max(f[k-1][i+n-(1<<(k-1))][j],f[k-1][i+n-(1<<(k-1))][j+n-(1<<(k-1))])); minn=min(min(f2[k-1][i][j],f2[k-1][i][j+n-(1<<(k-1))]),min(f2[k-1][i+n-(1<<(k-1))][j],f2[k-1][i+n-(1<<(k-1))][j+n-(1<<(k-1))])); ans=min(ans,maxn-minn); } printf("%d",ans); return 0; }