近期有价值题目记录3.9~3.15
1、jzoj5265 缘
题目大意:
给定两棵以1为根的树,i号点在第一棵树中编号为i,在第二棵树中编号为pi,选择一个点对(x,y),要求x!=y,最大化两点在两棵树中lca深度的和,输出和即可。
n<=1e5
题解:
考虑枚举第一棵树中的lca,设为x,问题就变成了求x的不同儿子的子树内点对在第二棵树中lca的最深是多少。
这个可以对儿子的子树一棵一棵地做,看做一个在dfs序上染色的过程,一开始都是白的,每次相当于询问当前点与其他黑点lca的深度最大值,这个最大值必然出现在当前点与前驱或者后继的lca。
这样我们就得到了一个n^2logn的做法,考虑启发式合并,每次先处理轻儿子,重儿子处理完不清空,再合并轻儿子,复杂度变为nlog^2n。
代码比较好写。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=1e5+10; int cnt=0,n,p[N],ans; int tot1=0,he1[N],ne1[N],t1[N]; void link1(int x,int y) { tot1++; ne1[tot1]=he1[x]; he1[x]=tot1; t1[tot1]=y; } int tot2=0,he2[N],ne2[N],t2[N]; void link2(int x,int y) { tot2++; ne2[tot2]=he2[x]; he2[x]=tot2; t2[tot2]=y; } int dfn1[N],fr1[N],sz[N],dep1[N],g[N]; void dfs1(int x) { cnt++; dfn1[x]=cnt; fr1[cnt]=x; sz[x]=1; for (int i=he1[x];i;i=ne1[i]) { dep1[t1[i]]=dep1[x]+1; dfs1(t1[i]); sz[x]=sz[x]+sz[t1[i]]; if (sz[t1[i]]>sz[g[x]]) g[x]=t1[i]; } } int dfn2[N],fr2[N],dep2[N],f[N][18]; void dfs2(int x) { cnt++; dfn2[x]=cnt; fr2[cnt]=x; for (int i=he2[x];i;i=ne2[i]) { dep2[t2[i]]=dep2[x]+1; f[t2[i]][0]=x; for (int j=1;j<=17;j++) f[t2[i]][j]=f[f[t2[i]][j-1]][j-1]; dfs2(t2[i]); } } int lca(int x,int y) { if (dep2[x]<=dep2[y]) swap(x,y); for (int i=17;i>=0;i--) if (dep2[f[x][i]]>=dep2[y]) x=f[x][i]; if (x==y) return x; for (int i=17;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int mx[N*4],mn[N*4]; void modify(int rt,int l,int r,int x,int y) { if (l==r) { if (y==-1) mx[rt]=0,mn[rt]=n+1; else mx[rt]=mn[rt]=y; return; } int mid=l+r>>1; if (x<=mid) modify(rt<<1,l,mid,x,y);else modify(rt<<1|1,mid+1,r,x,y); mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); mn[rt]=min(mn[rt<<1],mn[rt<<1|1]); } int get_max(int rt,int l,int r,int x,int y) { if (l>=x&&r<=y) return mx[rt]; int mid=l+r>>1; int ret=0; if (x<=mid) ret=max(ret,get_max(rt<<1,l,mid,x,y)); if (y>mid) ret=max(ret,get_max(rt<<1|1,mid+1,r,x,y)); return ret; } int get_min(int rt,int l,int r,int x,int y) { if (l>=x&&r<=y) return mn[rt]; int mid=l+r>>1; int ret=n+1; if (x<=mid) ret=min(ret,get_min(rt<<1,l,mid,x,y)); if (y>mid) ret=min(ret,get_min(rt<<1|1,mid+1,r,x,y)); return ret; } int calc(int x,int y) { if (!x) return -n; return dep2[lca(x,y)]; } void clean(int x) { for (int i=dfn1[x];i<=dfn1[x]+sz[x]-1;i++) modify(1,1,n,dfn2[p[fr1[i]]],-1); } void ins(int x) { for (int i=dfn1[x];i<=dfn1[x]+sz[x]-1;i++) { int u=get_max(1,1,n,1,dfn2[p[fr1[i]]]),v=get_min(1,1,n,dfn2[p[fr1[i]]],n); modify(1,1,n,dfn2[p[fr1[i]]],dfn2[p[fr1[i]]]); ans=max(ans,dep1[x]-1+calc(fr2[u],p[fr1[i]])); ans=max(ans,dep1[x]-1+calc(fr2[v],p[fr1[i]])); } } void solve(int x) { for (int i=he1[x];i;i=ne1[i]) if (t1[i]!=g[x]) { solve(t1[i]); clean(t1[i]); } if (g[x]) solve(g[x]); int u=get_max(1,1,n,1,dfn2[p[x]]),v=get_min(1,1,n,dfn2[p[x]],n); modify(1,1,n,dfn2[p[x]],dfn2[p[x]]); ans=max(ans,dep1[x]+calc(fr2[u],p[x])); ans=max(ans,dep1[x]+calc(fr2[v],p[x])); for (int i=he1[x];i;i=ne1[i]) if (t1[i]!=g[x]) ins(t1[i]); } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&p[i]); for (int i=2;i<=n;i++) { int x,y; scanf("%d%d",&x,&y); link1(x,i); link2(y,i); } dfs1(1); cnt=0; dfs2(1); ans=0; for (int i=1;i<=n;i++) modify(1,1,n,i,-1); solve(1); printf("%d\n",ans); }
2、newcoder上面的某题
题目大意:
给定n、N,求1~n的N次方的异或和。
n<=1e7,N<=1e9
题解:
考虑线性筛,如果当前数为质数,暴力求幂,否则可以被因子合并出来,复杂度O(n/ln*log)≈O(n)
代码很好写。
#include<bits/stdc++.h> using namespace std; const int N=1.3e7+10; int n,cnt; int prime[N]; long long f[N]; bool vis[N]; long long Pow(long long a,long long b,long long p) { long long result=1; long long base=a%p; while(b) { if(b&1) result=result*base%p; base=base*base%p; b=b>>1; } return result; } void Euler() { int mo=1e9+7; int ans=1; for(int i=2;i<=n;i++) { if(!vis[i]) { prime[++cnt]=i; f[i]=Pow(i,n,mo); } ans=ans^f[i]; for(int j=1;j<=cnt&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; f[i*prime[j]]=f[i]*f[prime[j]]%mo; if(i%prime[j]==0) break; //f[i*prime[j]]=f[i]*f[prime[j]]%mo; } } printf("%d\n",ans); } int main() { cin>>n; Euler(); return 0; }
3、jzoj6053 Mas的仙人掌
题目大意:
给定一棵树和m条边,这m条边有出现的概率,对于所有可能的图的价值为图上新加入的边在至多一个简单环中的个数,求这个价值的期望。
n,m<=1e6
题解:
一条边产生代价,只有在它两端到lca的路径上的连出去的在m条边中的所有边不出现。
考虑打标记,分类讨论...
代码很不好写。
#include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<map> using namespace std; const int N=1e6+10,mo=998244353; map<long long,int>mp1,mp2; int bz1[N],bz2[N],tag1[N],tag2[N]; int n,m,tot,he[N]; struct aa { int u,v,x,y,l,p,np; }q[N]; struct ee { int t,ne; }e[N*2]; void mul(int &x,int y) { x=(long long)x*y%mo; } int ksm(int x,int y) { int res=1; while (y) { if (y&1) mul(res,x); mul(x,x); y=y>>1; } return res; } void link(int x,int y) { tot++; e[tot].ne=he[x]; he[x]=tot; e[tot].t=y; } int fa[N],sz[N],son[N],dep[N],las[N]; void dfs1(int x,int y) { sz[x]=1; fa[x]=y; dep[x]=dep[y]+1; for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y) { int v=e[i].t; dfs1(v,x); sz[x]=sz[x]+sz[v]; if (sz[v]>sz[son[x]]) son[x]=v; } } void dfs2(int x,int y,int z) { las[x]=z; if (son[x]) dfs2(son[x],x,z); for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y&&e[i].t!=son[x]) dfs2(e[i].t,x,e[i].t); } int lca(int x,int y) { while (las[x]!=las[y]) { if (dep[las[x]]<dep[las[y]]) swap(x,y); x=fa[las[x]]; } if (dep[x]<dep[y]) return x; return y; } int get(int x,int y) { if (x==y) return 0; while (las[x]!=las[y]) { if (fa[las[x]]==y) return las[x]; x=fa[las[x]]; } return son[y]; } long long gethash(long long x,long long y) { if (x>y) swap(x,y); return (long long)1000000000*x+y; } void modify(aa x) { if (x.np) { int inv=ksm(x.np,mo-2); if (x.x) { mul(tag1[x.x],x.np); mul(tag2[x.u],x.np); mul(tag2[x.x],inv); } if (x.y) { mul(tag1[x.y],x.np); mul(tag2[x.v],x.np); mul(tag2[x.y],inv); } if (x.x&&x.y) { long long t=gethash(x.x,x.y); if (mp1.count(t)) mul(mp1[t],inv);else mp1[t]=inv; } }else { if (x.x) { bz1[x.x]++; bz2[x.u]++; bz2[x.x]--; } if (x.y) { bz1[x.y]++; bz2[x.v]++; bz2[x.y]--; } if (x.x&&x.y) --mp2[gethash(x.x,x.y)]; } } int solve(aa x) { int ans=x.p,flag=0; if (x.x) { flag=flag+bz1[x.u]-bz1[x.l]+bz2[x.x]; mul(ans,tag1[x.u]); mul(ans,ksm(tag1[x.l],mo-2)); mul(ans,tag2[x.x]); } if (x.y) { flag=flag+bz1[x.v]-bz1[x.l]+bz2[x.y]; mul(ans,tag1[x.v]); mul(ans,ksm(tag1[x.l],mo-2)); mul(ans,tag2[x.y]); } if (x.x&&x.y) { long long t=gethash(x.x,x.y); if (mp1.count(t)) mul(ans,mp1[t]); if (mp2.count(t)) flag=flag+mp2[t]; } if (x.np) mul(ans,ksm(x.np,mo-2));else flag--; if (flag) return 0; return ans; } void dfs(int x,int y) { for (int i=he[x];i;i=e[i].ne) if (e[i].t!=y) { int v=e[i].t; mul(tag1[v],tag1[x]); bz1[v]=bz1[v]+bz1[x]; dfs(v,x); mul(tag2[x],tag2[v]); bz2[x]=bz2[x]+bz2[v]; } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); link(x,y); link(y,x); } dfs1(1,0); dfs2(1,0,1); for (int i=0;i<N;++i) tag1[i]=tag2[i]=1; for (int i=1;i<=m;++i) { scanf("%d%d%d",&q[i].u,&q[i].v,&q[i].p); q[i].np=(1-q[i].p+mo)%mo; swap(q[i].p,q[i].np); q[i].l=lca(q[i].u,q[i].v); q[i].x=get(q[i].u,q[i].l); q[i].y=get(q[i].v,q[i].l); modify(q[i]); } int sum=0; dfs(1,0); for (int i=1;i<=m;++i) sum=(sum+solve(q[i]))%mo; printf("%d\n",sum); }
4.jzoj6058 fft
题目大意:
n道题答案为t,m道题答案为f,每次答题会立刻给出答案,以最优策略答题,问期望答错次数。
n、m<=5e5
题解:
数形结合一下,放在坐标系上,发现决策是一条不会超过y=x的折线。
考虑对答案的计算,每当到y=x上时都会产生1/2的贡献,算出每个到(x,x)的方案数即可。
代码很好写呢。
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int mo=998244353; const int N=1e6+10; long long jc[N],ny[N]; long long ksm(long long x,int y) { x=x%mo; long long v=1; while (y) { if (y&1) v=v*x%mo; x=x*x%mo; y=y>>1; } return v; } long long calc(int x,int y) { return jc[x]*ny[y]%mo*ny[x-y]%mo; } int main() { int n,m; scanf("%d%d",&n,&m); if (n<m) swap(n,m); jc[0]=1; for (int i=1;i<=n+m;i++) jc[i]=jc[i-1]*i%mo; ny[n+m]=ksm(jc[n+m],mo-2); for (int i=n+m-1;i>=1;i--) ny[i]=ny[i+1]*(i+1)%mo; ny[0]=1; long long ans=0; for (int i=1;i<=m;i++) ans=(ans+calc(2*i,i)*calc(n+m-2*i,m-i))%mo; ans=((ans*ksm(2*calc(n+m,m),mo-2)+n)%mo+mo)%mo; printf("%lld\n",(n+m-ans+mo)%mo); }
5.某个裸题
题目大意:
求树的拓扑序。
n<=1e5
题解:
结论,有根树的拓扑序等于
n!/∏size(所有子树)
换根即可。
代码很好写。
#include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; const int N=1e5+10,mo=998244353; int he[N],ne[N*2],t[N*2],tot=0,sz[N*2],n; long long jc[N],ny[N],ans,mul[N]; void link(int x,int y) { tot++; ne[tot]=he[x]; he[x]=tot; t[tot]=y; } long long ksm(long long x,int y) { long long ret=1; while (y) { if (y&1) ret=ret*x%mo; y=y>>1; x=x*x%mo; } return ret; } void dfs(int x,int y) { sz[x]=1; mul[x]=1; for (int i=he[x];i;i=ne[i]) if (t[i]!=y) { dfs(t[i],x); sz[x]=sz[x]+sz[t[i]]; mul[x]=mul[x]*mul[t[i]]%mo; } mul[x]=mul[x]*ny[sz[x]]%mo*jc[sz[x]-1]%mo; } void solve(int x,int y) { ans=(ans+jc[n]*mul[x]%mo)%mo; for (int i=he[x];i;i=ne[i]) if (t[i]!=y) { mul[t[i]]=mul[x]*ny[n-sz[t[i]]]%mo*jc[n-sz[t[i]]-1]%mo*ny[sz[t[i]]-1]%mo*jc[sz[t[i]]]%mo; solve(t[i],x); } } int main() { scanf("%d",&n); for (int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); link(x,y); link(y,x); } jc[0]=1; for (int i=1;i<=n;i++) jc[i]=(long long)jc[i-1]*i%mo; ny[n]=ksm(jc[n],mo-2); for (int i=n-1;i>=0;i--) ny[i]=(long long)ny[i+1]*(i+1)%mo; dfs(1,0); ans=0; solve(1,0); printf("%lld\n",ans); }
6.codeplus6 hard组T1
题目大意:
给出一个序列a[],q组询问l,r,求:
∏(i=l...r-1)∏(j=i+1...r) gcd(a[i],a[j])
n、q、a[i]<=1e5
题解:
分质因子计算答案。
值域比较小,可以考虑分类讨论,分为质因子大于√1e5的和小于等于的。
两部分的答案分别计算,最后乘在一起就好了。
小于等于的部分,答案与每种次数出现的次数有关,O(n√n *log)统计,每次询问是O(√n *log)的。
大于的部分,次数最多为1,考虑分块。
我的分块方法比较猎奇,设块大小为k,对1、1+k、1+2k.....为开头维护区间内的答案。
询问长度小于k的直接暴力,否则找到第一个大于等于l的开头,将前面的暴力。
单次询问复杂度O(√n *log)
暴力的过程我写了一个权值线段树...
总复杂度是O(n√n *log)的。
空间似乎比较也是这么大的,当然优化方法有很多...不累述。
代码想好了之后很好写。
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int N=1e5+10,mo=998244353; long long a[N],b[N]; long long mi[66][18]; int vis[N]; long long sz[N]; int c[N][66]; int s[N][66][10],s2[N][20]; int ans[320][N]; long long sum[N]; long long ksm(long long x,int y) { long long ret=1; while (y) { if (y&1) ret=ret*x%mo; x=x*x%mo; y=y>>1; } return ret; } int g[N],tr[N*20],L[N*20],R[N*20],nd=0; void ins(int &rt,int l,int r,int x) { if (!rt) { nd++; rt=nd; } if (l==r) { tr[rt]=1; return; } int mid=l+r>>1; if (x<=mid) ins(L[rt],l,mid,x); else ins(R[rt],mid+1,r,x); tr[rt]=tr[L[rt]]+tr[R[rt]]; } int query(int rt,int l,int r,int x,int y) { if (!rt) return 0; if (l>=x&&r<=y) return tr[rt]; int mid=l+r>>1; int ret=0; if (x<=mid) ret=ret+query(L[rt],l,mid,x,y); if (y>mid) ret=ret+query(R[rt],mid+1,r,x,y); return ret; } int main() { int n,q; scanf("%d%d",&n,&q); for (int i=1;i<=n;i++) scanf("%lld",&a[i]); int mx=0,tot=0; for (int i=2;i<=100000;i++) { if (!vis[i]) { tot++; sz[tot]=i; if (i<=316) mx=tot; } for (int j=i;j<=100000;j=j+i) vis[j]=1; } for (int i=1;i<=mx;i++) { mi[i][0]=1; for (int j=1;j<=17;j++) mi[i][j]=mi[i][j-1]*sz[i]%mo; } for (int i=1;i<=n;i++) { b[i]=a[i]; for (int j=1;j<=tot;j++) if (sz[j]*sz[j]<=100000) { while (b[i]%sz[j]==0) { c[i][j]++; b[i]=b[i]/sz[j]; } }else break; for (int j=2;j<=mx;j++) { for (int k=0;k<=9;k++) s[i][j][k]=s[i-1][j][k]; s[i][j][c[i][j]]++; } for (int k=0;k<=17;k++) s2[i][k]=s2[i-1][k]; s2[i][c[i][1]]++; a[i]=a[i]/b[i]; if (b[i]!=1) ins(g[b[i]],1,n,i); } int kk=sqrt(n); for (int i=1;(i-1)*kk+1<=n;i++) { for (int j=1;j<=100000;j++) sum[j]=0; ans[i][(i-1)*kk]=1; for (int j=(i-1)*kk+1;j<=n;j++) { if (sum[b[j]]) ans[i][j]=(long long)ans[i][j-1]*sum[b[j]]%mo; else ans[i][j]=ans[i][j-1]; if (sum[b[j]]) sum[b[j]]=sum[b[j]]*b[j]%mo;else sum[b[j]]=b[j]; } } for (int i=1;i<=q;i++) { int l,r; scanf("%d%d",&l,&r); long long mul=1; for (int j=2;j<=mx;j++) { long long len=r-l; for (int k=0;k<=9;k++) { long long tmp=s[r][j][k]-s[l-1][j][k]; long long gg=(len+len-tmp+1)*tmp/2; mul=mul*ksm(mi[j][k],gg)%mo; len=len-tmp; } } long long len=r-l; for (int k=0;k<=17;k++) { long long tmp=s2[r][k]-s2[l-1][k]; long long gg=(len+len-tmp+1)*tmp/2; mul=mul*ksm(mi[1][k],gg)%mo; len=len-tmp; } if (r-l+1<=kk) { for (int j=l;j<=r;j++) if (b[j]!=1) { int tmp=query(g[b[j]],1,n,j+1,r); mul=mul*ksm(b[j],tmp)%mo; } }else { int j=1; while ((j-1)*kk+
- ASP.NET Core File Providers
- JS魔法堂之实战:纯前端的图片预览
- 域名publica.com近50万交易 买家身份浮出水面
- Vim杂记:Sublime的配色方案
- Vim杂记:markdown插件
- 【手把手教你全文检索】Apache Lucene初探
- 微信小程序的“小游戏” 是什么?
- CentOS6.5菜鸟之旅:中文编辑器忍痛放弃Sublime
- JS魔法堂:Data URI Scheme介绍
- Sedo榜:域名happymath.com以3.5万美金交易居榜首
- ASP.NET Core WebListener 服务器
- 雪花代码
- 【插件开发】—— 3 透视图开发
- ASP.NET Core模块概述
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 基于Android studio3.6的JNI教程之helloworld思路详解
- 基于Android studio3.6的JNI教程之opencv实例详解
- AndroidStudio代码达到指定字符长度时自动换行实例
- android studio 新建项目报错的解决之路
- Android Studio 3.6中使用视图绑定替代 findViewById的方法
- Android 使用View Binding的方法详解
- CentOS一键安装Resilio Sync脚本
- Python从URL获取图片、读取图片格式并保存到本地
- Linux一键屏蔽指定国家所有的IP访问
- React基础(9)-React中发送Ajax请求以及Mock数据
- 一个快速引入CDN的workflow
- 在Ryzen平台上安装macOS High Sierra苹果系统
- v-selectpage 基于Vue2的高清重制版
- Android Studio3.6中的View Binding初探及用法区别
- Android日志文件的读写工具类