高一上八月中旬日记
8.11
闲话
- 上午 \(7:30 \sim 11:30\) @worldvanquisher | @joke3579 学长安排了一场模拟赛。
- 午休 \(field\) 查宿。
- 下午去机房的路上,迷迷糊糊地就进了电梯,这时候才意识到电梯有 \(miaomiao\) 和 \(field\) ;到机房时发现装在袋子里的花露水丢了,离谱。
- 讲题前 \(miaomiao\) 跟我们说今天题出难了,实在改不动就不改了;博客啥的建议写写,总结总结,免得出现以后遇到原题还不会做的情况,说以前就出现过这种事情;把博客链接挂到个人介绍,博客设密码的也得告诉他,他也想看看我们都写点啥(估计是跟 \(huge,feifei\) 学的)。
- 晚休 \(field\) 查宿。我们宿舍里 \(4\) 人在我床上吃泡面时,看见 \(field\) 在门外闪了闪手机,经过 @xrlong 的一番辩解成功化险为夷。
做题纪要
[ABC302G] Sort from 1 to 4
- 多倍经验: luogu P1459 [USACO2.1] 三值的排序 Sorting a Three-Valued Sequence
- 详见 暑假集训CSP提高模拟18 T1 P227. Mortis 。
P224. 无标号 Multiset 构造
luogu P8511 [Ynoi Easy Round 2021] TEST_68
luogu P10858 [HBCPC2024] Long Live
-
由 \(a \sqrt{b}=\sqrt{\frac{\operatorname{lcm}(x,y)}{\gcd(x,y)}}\) 有 \(a^{2}b=\frac{\operatorname{lcm}(x,y)}{\gcd(x,y)}\) ,移项得 \(ab=\frac{\frac{\operatorname{lcm}(x,y)}{\gcd(x,y)}}{a}\) 。
-
当 \(a=1\) 时,有 \(ab=\frac{\operatorname{lcm}(x,y)}{\gcd(x,y)}\) 取到最大值。
点击查看代码
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } ll lcm(ll a,ll b) { return a/gcd(a,b)*b; } int main() { ll t,x,y,i; cin>>t; for(i=1;i<=t;i++) { cin>>x>>y; cout<<1<<" "<<lcm(x,y)/gcd(x,y)<<endl; } return 0; }
luogu P10862 [HBCPC2024] Spicy or Grilled?
-
\(xb+(n-x)a\) 即为所求。
点击查看代码
int main() { ll t,n,x,a,b,i; cin>>t; for(i=1;i<=t;i++) { cin>>n>>x>>a>>b; cout<<x*b+(n-x)*a<<endl; } return 0; }
luogu P10867 [HBCPC2024] Points on the Number Axis A
-
感性理解:因为每个点被选择的概率是一样的,把式子拆开并合并后系数都是 \(\frac{1}{n}\) 。
-
最终有 \(\frac{\sum\limits_{i=1}^{n}x_{i}}{n}\) 即为所求。
点击查看代码
const ll p=998244353; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } int main() { ll n,sum=0,x,i; cin>>n; for(i=1;i<=n;i++) { cin>>x; sum=(sum+x)%p; } cout<<sum*qpow(n,p-2,p)%p<<endl; return 0; }
luogu P4735 最大异或和
-
对 \(\{ a \}\) 作异或前缀和,操作 \(2\) 等价于求 \(\max\limits_{i=l}^{r}\{ s_{n} \bigoplus x \bigoplus s_{i-1} \}=\max\limits_{i=l-1}^{r-1}\{ s_{n} \bigoplus x \bigoplus s_{i} \}\) 。
-
可持久化 \(Trie\) 树板子。
- 类似主席树的建树方法,每次新建一条链。递归或不递归写法差不多。
- 在末尾记一下数量,前缀相减判断有没有。
- 注意插入一个 \(a_{0}=0\) 。
- 空间复杂度为 \(O(|\sum| \times \sum\limits_{i=1}^{n}|s_{i}|)\) 。
点击查看非递归代码
struct PDS_Trie { int root[600010],rt_sum=0; struct Trie { int cnt,ch[2]; }tree[600010<<5]; int build() { rt_sum++; return rt_sum; } void insert(int pre,int &rt,int s) { rt=build(); int p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(int i=25;i>=0;i--) { for(int j=0;j<=1;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[(s>>i)&1]=build(); p=tree[p].ch[(s>>i)&1]; q=tree[q].ch[(s>>i)&1]; tree[p].cnt=tree[q].cnt+1; } } int query(int rt1,int rt2,int x) { int ans=0; for(int i=25;i>=0;i--) { if(tree[tree[rt2].ch[((x>>i)&1)^1]].cnt-tree[tree[rt1].ch[((x>>i)&1)^1]].cnt>=1)//前缀相减 //if(tree[rt2].ch[((x>>i)&1)^1]-tree[rt1].ch[((x>>i)&1)^1]>=1)//写成这样也是可以的,若编号不一样说明新建了节点,相应的 cnt 会至少多 1 { ans|=(1<<i); rt1=tree[rt1].ch[((x>>i)&1)^1]; rt2=tree[rt2].ch[((x>>i)&1)^1]; } else { rt1=tree[rt1].ch[(x>>i)&1]; rt2=tree[rt2].ch[(x>>i)&1]; } } return ans; } }T; int main() { int n,m,l,r,x,sum=0,pos=1,i; char pd[5]; scanf("%d%d",&n,&m); T.insert(T.root[0],T.root[1],0); for(i=1;i<=n;i++) { scanf("%d",&x); sum^=x; pos++; T.insert(T.root[pos-1],T.root[pos],sum); } for(i=1;i<=m;i++) { scanf("%s",pd+1); if(pd[1]=='A') { scanf("%d",&x); sum^=x; pos++; T.insert(T.root[pos-1],T.root[pos],sum); } else { scanf("%d%d%d",&l,&r,&x); l++; r++; printf("%d\n",T.query(T.root[l-1-1],T.root[r-1],sum^x)); } } return 0; }
luogu P6088 [JSOI2015] 字符串树
-
建出可持久化 \(Trie\) 树后树上前缀和维护。
点击查看代码
struct node { int nxt,to; string w; }e[200010]; int head[100010],fa[100010],siz[100010],dep[100010],son[100010],top[100010],cnt=0; string s,t; void add(int u,int v,string w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct PDS_Trie { int root[100010],rt_sum=0; struct Trie { int ch[27],cnt; }tree[100010*15]; int val(char x) { return x-'a'+1; } int build() { rt_sum++; return rt_sum; } void insert(int pre,int &rt,string s) { rt=build(); int p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(int i=0;i<s.size();i++) { for(int j=1;j<=26;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[val(s[i])]=build(); p=tree[p].ch[val(s[i])]; q=tree[q].ch[val(s[i])]; tree[p].cnt=tree[q].cnt+1; } } int query(int rt,string s) { for(int i=0;i<s.size();i++) { if(tree[rt].ch[val(s[i])]==0) { return 0; } rt=tree[rt].ch[val(s[i])]; } return tree[rt].cnt; } }T; void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { T.insert(T.root[x],T.root[e[i].to],e[i].w); dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } int main() { int n,m,u,v,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v>>s; add(u,v,s); add(v,u,s); } dfs1(1,0); dfs2(1,1); cin>>m; for(i=1;i<=m;i++) { cin>>u>>v>>s; cout<<T.query(T.root[u],s)+T.query(T.root[v],s)-2*T.query(T.root[lca(u,v)],s)<<endl; } return 0; }
8.12
闲话
- 上午 \(7:30 \sim 11:30\) @Chen_jr 学长安排了一场模拟赛。
- 午休 \(miaomiao\) 查宿。
- 下午讲完题后 \(miaomiao\) 发了雪糕。
- 晚休 \(miaomiao\) 查宿。
做题纪要
luogu P5838 [USACO19DEC] Milk Visits G
-
建出主席树后树上前缀和维护。
点击查看代码
struct PDS_SMT { int root[200010],rt_sum; struct SegmentTree { int ls,rs,cnt; }tree[200010<<5]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void build_tree(int &rt,int l,int r) { rt=build_rt(); if(l==r) { tree[rt].cnt=0; return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); } void update(int pre,int &rt,int l,int r,int pos) { rt=build_rt(); tree[rt]=tree[pre]; if(l==r) { tree[rt].cnt++; return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos); } else { update(rson(pre),rson(rt),mid+1,r,pos); } } int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int pos) { if(l==r) { return tree[rt1].cnt+tree[rt2].cnt-tree[rt3].cnt-tree[rt4].cnt>=1; } int mid=(l+r)/2; if(pos<=mid) { return query(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,pos); } else { return query(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,pos); } } }T; struct node { int nxt,to; }e[200010]; int head[200010],w[200010],fa[200010],siz[200010],dep[200010],son[200010],top[200010],cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father,int n) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; T.update(T.root[father],T.root[x],1,n,w[x]); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,n); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return (dep[u]<dep[v])?u:v; } int main() { int n,m,u,v,c,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>w[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } T.build_tree(T.root[0],1,n); dfs1(1,0,n); dfs2(1,1); for(i=1;i<=m;i++) { cin>>u>>v>>c; cout<<T.query(T.root[u],T.root[v],T.root[lca(u,v)],T.root[fa[lca(u,v)]],1,n,c); } return 0; }
BZOJ3956 Count
-
产生的贡献同 luogu P3722 [AH2017/HNOI2017] 影魔 的 \(p_{1}\) 。
-
一对点对只会产生一次贡献,最后去个重就行了。
点击查看代码
struct quality { int l,r,w; bool operator == (const quality &another) const { return (l==another.l&&r==another.r&&w==another.w); } }; vector<quality>e[300010]; int a[300010],l[300010],r[300010]; stack<int>s; void add(int u,int l,int r,int w) { e[u].push_back((quality){l,r,w}); } struct PDS_SMT { int root[300010],rt_sum; struct SegmentTree { int ls,rs,sum,lazy; }tree[300010<<6]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void build_tree(int &rt,int l,int r) { rt=build_rt(); if(l==r) { return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); } void update(int pre,int &rt,int l,int r,int x,int y,int val) { rt=(rt==0)?rt:build_rt(); tree[rt]=tree[pre]; tree[rt].sum+=val*(min(r,y)-max(l,x)+1); if(x<=l&&r<=y) { tree[rt].lazy+=val; return; } int mid=(l+r)/2; if(x<=mid) { update(lson(pre),lson(rt),l,mid,x,y,val); } if(y>mid) { update(rson(pre),rson(rt),mid+1,r,x,y,val); } } int query(int rt1,int rt2,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt2].sum-tree[rt1].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt1),lson(rt2),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y); } return ans+(tree[rt2].lazy-tree[rt1].lazy)*(min(r,y)-max(l,x)+1); } }T; int main() { int n,m,type,len,x,y,ans=0,i,j; scanf("%d%d%d",&n,&m,&type); for(i=1;i<=n;i++) { scanf("%d",&a[i]); while(s.empty()==0&&a[s.top()]<=a[i]) { s.pop(); } l[i]=(s.empty()==0)?s.top():0; s.push(i); } while(s.empty()==0) { s.pop(); } for(i=n;i>=1;i--) { while(s.empty()==0&&a[s.top()]<=a[i]) { s.pop(); } r[i]=(s.empty()==0)?s.top():n+1; s.push(i); } for(i=1;i<=n;i++) { if(i!=n) { add(i,i+1,i+1,1); } if(1<=l[i]&&r[i]<=n) { add(l[i],r[i],r[i],1); } } T.build_tree(T.root[0],1,n); for(i=1;i<=n;i++) { T.root[i]=T.root[i-1]; len=unique(e[i].begin(),e[i].end())-e[i].begin(); for(j=0;j<len;j++) { T.update(T.root[i],T.root[i],1,n,e[i][j].l,e[i][j].r,e[i][j].w); } } for(i=1;i<=m;i++) { scanf("%d%d",&x,&y); x=(type==0)?x:(x+ans-1)%n+1; y=(type==0)?y:(y+ans-1)%n+1; if(x>y) { swap(x,y); } ans=T.query(T.root[x-1],T.root[y],1,n,x,y); printf("%d\n",ans); } return 0; }
CF1517C Fillomino 2
P160. 那一天她离我而去
luogu P6175 无向图的最小环问题
-
最小环板子。
- 考虑 \(Floyd\) 算法的过程,\(f_{k,i,j}\) 表示经过编号不经过 \(k\) 的节点,从 \(i\) 到 \(j\) 的最短路长度,由此就有了将 \(k\) 看做中转点的理解。
- 同样的,在更新 \(f_{k, \dots , \dots }\) 之前,枚举环上与 \(k\) 相连的点 \(i,j\) 用 \(f_{k-1,i,j}+w_{j,k}+w_{k,i}\) 更新答案。
点击查看代码
int f[110][110],a[110][110]; int main() { int n,m,u,v,w,ans=0x7f7f7f7f,i,j,k; cin>>n>>m; memset(a,0x3f,sizeof(a)); memset(f,0x3f,sizeof(f)); for(i=1;i<=m;i++) { cin>>u>>v>>w; f[u][v]=f[v][u]=a[u][v]=a[v][u]=min(f[u][v],w); } for(k=1;k<=n;k++) { for(i=1;i<=k-1;i++) { for(j=1;j<=k-1;j++) { if(i!=j&&f[i][j]!=0x3f3f3f3f&&a[j][k]!=0x3f3f3f3f&&a[k][i]!=0x3f3f3f3f) { ans=min(ans,f[i][j]+a[j][k]+a[k][i]); } } } for(i=1;i<=n;i++) { if(i!=k&&f[i][k]!=0x3f3f3f3f) { for(j=1;j<=n;j++) { if(i!=j&&f[k][j]!=0x3f3f3f3f) { f[i][j]=min(f[i][j],f[i][k]+f[k][j]); } } } } } if(ans==0x7f7f7f7f) { cout<<"No solution."<<endl; } else { cout<<ans<<endl; } return 0; }
CF1144G Two Merged Sequences
-
设 \(f_{i,0/1}\) 表示将前 \(i\) 个数拆成一个递增序列和一个递减序列后,当 \(a_{i}\) 属于递增/递减序列时,递减/递增序列末尾可能的最大值/最小值。初始化为 \(\begin{cases} f_{1,0}=f_{i,1}=\infty & i \in (1,n] \\ f_{1,1}=f_{i,0}=-\infty & i \in (1,n] \end{cases}\)
-
大力分讨,式子有点绕,过于抽象,凑活看吧。
- 若 \(a_{i-1},a_{i}\) 均属于递增序列,当且仅当 \(a_{i-1}<a_{i}\) ,此时有 \(f_{i,0}=\max(f_{i,0},f_{i-1,0})\) 。
- 若 \(a_{i-1}\) 属于递减序列, \(a_{i}\) 属于递增序列,当且仅当 \(f_{i-1,1}<a_{i}\) ,此时有 \(f_{i,0}=\max(f_{i,0},a_{i-1})\) 。
- 取递增序列末尾前一个来判断是否递增。
- 若 \(a_{i-1},a_{i}\) 均属于递减序列,当且仅当 \(a_{i-1}>a_{i}\) ,此时有 \(f_{i,1}=\min(f_{i,1},f_{i-1,1})\) 。
- 若 \(a_{i-1}\) 属于递增序列, \(a_{i}\) 属于递减序列,当且仅当 \(f_{i-1,0}>a_{i}\) ,此时有 \(f_{i,1}=\min(f_{i,1},a_{i-1})\) 。
- 取递增序列末尾前一个来判断是否递增。
-
记 \(g_{i,0/1}\) 表示当 \(f_{i,0/1}\) 取到最优情况时 \(a_{i-1}\) 属于哪个序列。
-
最后倒序输出即可。
点击查看代码
int a[200010],f[200010][2],g[200010][2],ans[200010]; int main() { int n,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; f[i][0]=-0x7f7f7f7f; f[i][1]=0x7f7f7f7f; } f[1][0]=0x7f7f7f7f; f[1][1]=-0x7f7f7f7f; for(i=2;i<=n;i++) { if(a[i-1]<a[i]) { if(f[i-1][0]>f[i][0]) { f[i][0]=f[i-1][0]; g[i][0]=0; } } if(f[i-1][1]<a[i]) { if(a[i-1]>f[i][0]) { f[i][0]=a[i-1]; g[i][0]=1; } } if(a[i-1]>a[i]) { if(f[i-1][1]<f[i][1]) { f[i][1]=f[i-1][1]; g[i][1]=1; } } if(f[i-1][0]>a[i]) { if(a[i-1]<f[i][1]) { f[i][1]=a[i-1]; g[i][1]=0; } } } if(f[n][0]>-0x7f7f7f7f||f[n][1]<0x7f7f7f7f) { cout<<"YES"<<endl; ans[n]=(f[n][1]<0x7f7f7f7f); for(i=n;i>=2;i--) { ans[i-1]=g[i][ans[i]]; } for(i=1;i<=n;i++) { cout<<ans[i]<<" "; } } else { cout<<"NO"<<endl; } return 0; }
8.13
闲话
- 上午 @Muel_imj 讲了李超树, @H_Kaguya 讲了可持久化数据结构和珂朵莉树,并透露一道珂朵莉树例题被拉去当成模拟赛 \(T4\) 了,所以没有细讲。
- 下午写可持久化 \(Trie\) 树给写恼了。
- 晚上 \(feifei\) 在 \(ftp\) 里放了一堆和巴黎奥运会相关的“课外读物”,因为是 jpg 后缀,所以在 \(Linux\) 下随机能看,说让我们感兴趣的可以读读。
做题纪要
P175. 哪一天她能重回我身边
luogu P4098 [HEOI2013] ALO
-
设 \(l_{i,0/1},r_{i,0/1}\) 分别表示 \(i\) 左边第一个/第二个比 \(a_{i}\) 小的位置和 \(i\) 右边第一个/第二个比 \(a_{i}\) 小的位置,单调栈处理第一个位置,\(ST\) 表二分维护第二个位置即可。
- 具体地,以 \(l\) 为例,在 \([1,l_{i,0})\) 种找到一个最靠右的位置 \(pos\) 使得 \([pos,l_{i,0})\) 的最大值 \(>a_{i}\) ,即 \(a_{pos}\) 取 \([pos,l_{i,0})\) 的最大值时,令 \(l_{i,1}=pos\) 。
-
那么 \(a_{i}\) 就会作为 \((l_{i,0},r_{i,1})\) 和 \((l_{i,1},r_{i,0})\) 的次大值,可持久化 \(Trie\) 树维护最大异或和即可。
-
特判下序列的最大值不能作为次大值,不然的话就特殊处理下边界。
点击查看代码
int a[50010],l[50010][2],r[50010][2]; stack<int>s; struct PDS_Trie { int root[50010],rt_sum=0; struct Trie { int ch[2],cnt; }tree[50010*32]; int build() { rt_sum++; return rt_sum; } void insert(int pre,int &rt,int s) { rt=build(); int p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(int i=30;i>=0;i--) { for(int j=0;j<=1;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[(s>>i)&1]=build(); p=tree[p].ch[(s>>i)&1]; q=tree[q].ch[(s>>i)&1]; tree[p].cnt=tree[q].cnt+1; } } int query(int rt1,int rt2,int x) { int ans=0; for(int i=30;i>=0;i--) { if(tree[rt2].ch[((x>>i)&1)^1]-tree[rt1].ch[((x>>i)&1)^1]>=1) { ans|=(1<<i); rt1=tree[rt1].ch[((x>>i)&1)^1]; rt2=tree[rt2].ch[((x>>i)&1)^1]; } else { rt1=tree[rt1].ch[(x>>i)&1]; rt2=tree[rt2].ch[(x>>i)&1]; } } return ans; } }T; struct ST { int fmaxx[50010][20]; void init(int n,int a[]) { for(int i=1;i<=n;i++) { fmaxx[i][0]=a[i]; } for(int j=1;j<=log2(n);j++) { for(int i=1;i+(1<<j)-1<=n;i++) { fmaxx[i][j]=max(fmaxx[i][j-1],fmaxx[i+(1<<(j-1))][j-1]); } } } int query(int l,int r) { int t=log2(r-l+1); return max(fmaxx[l][t],fmaxx[r-(1<<t)+1][t]); } }S; int divede1(int x,int y,int pos,int ans) { int l=x,r=y,mid; while(l<=r) { mid=(l+r)/2; if(S.query(mid,y)>a[pos]) { ans=mid; l=mid+1; } else { r=mid-1; } } return ans; } int divede2(int x,int y,int pos,int ans) { int l=x,r=y,mid; while(l<=r) { mid=(l+r)/2; if(S.query(x,mid)>a[pos]) { ans=mid; r=mid-1; } else { l=mid+1; } } return ans; } int main() { int n,ans=0,maxx=0,i; cin>>n; T.insert(T.root[0],T.root[0],0); for(i=1;i<=n;i++) { cin>>a[i]; maxx=max(maxx,a[i]); T.insert(T.root[i-1],T.root[i],a[i]); while(s.empty()==0&&a[s.top()]<=a[i]) { s.pop(); } l[i][0]=(s.empty()==0)?s.top():0; s.push(i); } S.init(n,a); while(s.empty()==0) { s.pop(); } for(i=n;i>=1;i--) { while(s.empty()==0&&a[s.top()]<=a[i]) { s.pop(); } r[i][0]=(s.empty()==0)?s.top():n+1; s.push(i); l[i][1]=divede1(1,l[i][0]-1,i,0); r[i][1]=divede2(r[i][0]+1,n,i,n+1); } for(i=1;i<=n;i++) { if(a[i]!=maxx) { ans=max(ans,T.query(T.root[l[i][0]+1-1],T.root[r[i][1]-1],a[i])); ans=max(ans,T.query(T.root[l[i][1]+1-1],T.root[r[i][0]-1],a[i])); } } cout<<ans<<endl; return 0; }
luogu P4592 [TJOI2018] 异或
-
\(DFS\) 序排成序列后查询路径和子树信息,可持久化 \(Trie\) 树维护。
点击查看代码
struct PDS_Trie { ll root[200010],rt_sum=0; struct Trie { ll cnt,ch[2]; }tree[200010*32]; ll build() { rt_sum++; return rt_sum; } void insert(ll pre,ll &rt,ll s) { rt=build(); int p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(ll i=30;i>=0;i--) { for(ll j=0;j<=1;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[(s>>i)&1]=build(); p=tree[p].ch[(s>>i)&1]; q=tree[q].ch[(s>>i)&1]; tree[p].cnt=tree[q].cnt+1; } } ll query(ll rt1,ll rt2,ll s) { ll ans=0; for(ll i=30;i>=0;i--) { if(tree[rt2].ch[((s>>i)&1)^1]-tree[rt1].ch[((s>>i)&1)^1]>=1) { ans|=(1<<i); rt1=tree[rt1].ch[((s>>i)&1)^1]; rt2=tree[rt2].ch[((s>>i)&1)^1]; } else { rt1=tree[rt1].ch[(s>>i)&1]; rt2=tree[rt2].ch[(s>>i)&1]; } } return ans; } }T; struct node { ll nxt,to; }e[200010]; ll head[200010],w[200010],siz[200010],fa[200010],dep[200010],son[200010],top[200010],dfn[200010],out[200010],tot=0,cnt=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(ll x,ll father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll id) { top[x]=id; tot++; dfn[x]=tot; T.insert(T.root[tot-1],T.root[tot],w[x]); if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=son[x]&&e[i].to!=fa[x]) { dfs2(e[i].to,e[i].to); } } } out[x]=tot; } ll query1(ll u,ll z) { return T.query(T.root[dfn[u]-1],T.root[out[u]],z); } ll query2(ll u,ll v,ll z) { ll ans=0; while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { ans=max(ans,T.query(T.root[dfn[top[u]]-1],T.root[dfn[u]],z)); u=fa[top[u]]; } else { ans=max(ans,T.query(T.root[dfn[top[v]]-1],T.root[dfn[v]],z)); v=fa[top[v]]; } } if(dep[u]<dep[v]) { ans=max(ans,T.query(T.root[dfn[u]-1],T.root[dfn[v]],z)); } else { ans=max(ans,T.query(T.root[dfn[v]-1],T.root[dfn[u]],z)); } return ans; } int main() { ll n,m,pd,u,v,z,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>w[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(1,0); T.insert(T.root[0],T.root[0],0); dfs2(1,1); for(i=1;i<=m;i++) { cin>>pd>>u; if(pd==1) { cin>>z; cout<<query1(u,z)<<endl; } else { cin>>v>>z; cout<<query2(u,v,z)<<endl; } } return 0; }
luogu P5283 [十二省联考 2019] 异或粽子
-
同 luogu P2048 [NOI2010] 超级钢琴 | luogu P1631 序列合并 ,我们在取出最优解后放入次优解进行更新。
-
具体地,对于一个右端点 \(r\) ,在 \([0,r-1]\) 中找到一个数 \(s_{l}\) 使得 \(s_{l} \bigoplus s_{r}\) 最大,并加入大根堆,记录左端点 \(pos\) 所属区间 \([0,r-1]\) ,异或值 \(sum=s_{l} \bigoplus s_{r}\) 。
-
将 \((pos,l,r,sum)\) 取出后将状态分割为 \((pos',l,pos-1,sum')\) 和 \((pos'',pos+1,r,sum'')\) 放入大根堆。
-
注意向右移动数组。
点击查看代码
struct PDS_Trie { ll root[500010],rt_sum=0; struct Trie { ll ch[2],cnt,id; }tree[500010*35]; ll build() { rt_sum++; return rt_sum; } void insert(ll pre,ll &rt,ll s,ll id) { rt=build(); ll p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(ll i=32;i>=0;i--) { for(ll j=0;j<=1;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[(s>>i)&1]=build(); p=tree[p].ch[(s>>i)&1]; q=tree[q].ch[(s>>i)&1]; tree[p].cnt=tree[q].cnt+1; } tree[p].id=id; } pair<ll,ll> query(ll rt1,ll rt2,ll s) { ll ans=0; for(ll i=32;i>=0;i--) { if(tree[rt2].ch[((s>>i)&1)^1]-tree[rt1].ch[((s>>i)&1)^1]>=1) { ans|=(1ll<<i); rt1=tree[rt1].ch[((s>>i)&1)^1]; rt2=tree[rt2].ch[((s>>i)&1)^1]; } else { rt1=tree[rt1].ch[(s>>i)&1]; rt2=tree[rt2].ch[(s>>i)&1]; } } return make_pair(ans,tree[rt2].id); } }T; ll a[500010],sum[500010]; struct node { ll pos,l,r,val,x; bool operator < (const node &another) const { return val<another.val; } }tmp; priority_queue<node>q; int main() { ll n,k,ans=0,pos=1,i; pair<ll,ll>s; cin>>n>>k; T.insert(T.root[0],T.root[1],0,pos); for(i=2;i<=n+1;i++) { cin>>a[i]; sum[i]=sum[i-1]^a[i]; pos++; T.insert(T.root[pos-1],T.root[pos],sum[i],pos); s=T.query(T.root[1-1],T.root[pos-1],sum[i]); q.push((node){s.second,1,pos-1,s.first,pos}); } for(i=1;i<=k;i++) { tmp=q.top(); q.pop(); ans+=tmp.val; if(tmp.l<=tmp.pos-1) { s=T.query(T.root[tmp.l-1],T.root[tmp.pos-1],sum[tmp.x]); q.push((node){s.second,tmp.l,tmp.pos-1,s.first,tmp.x}); } if(tmp.pos+1<=tmp.r) { s=T.query(T.root[tmp.pos+1-1],T.root[tmp.r],sum[tmp.x]); q.push((node){s.second,tmp.pos+1,tmp.r,s.first,tmp.x}); } } cout<<ans<<endl; return 0; }
CF1055F Tree and XOR
-
先做一遍树上前缀异或和。
-
考虑从高到低枚举答案的每一位,计算这一位选 \(0\) 或 \(1\) 各有多少对,然后在 \(Trie\) 树上二分。
-
具体地,每个数维护一个指针表示与自己匹配的链,看下一步能不能走到使这一位异或和为 \(0\) 的儿子。
-
因为卡空间,所以需要滚动数组来优化 \(Trie\) 树。
点击查看代码
vector<pair<ll,ll> >e[2000010]; ll s[2000010],trie[2000010][2],siz[2000010],trie_old[2000010],pos[2000010],tot; void add(ll u,ll v,ll w) { e[u].push_back(make_pair(v,w)); } void dfs(ll x) { for(ll i=0;i<e[x].size();i++) { s[e[x][i].first]=s[x]^e[x][i].second; dfs(e[x][i].first); } } int main() { ll n,k,u,v,w,cnt=0,i,j; ull ans=0; scanf("%lld%lld",&n,&k); for(i=2;i<=n;i++) { scanf("%lld%lld",&u,&w); v=i; add(u,v,w); } dfs(1); for(i=1;i<=n;i++) { trie_old[i]=pos[i]=1; } for(i=62;i>=0;i--) { for(j=1;j<=tot;j++) { siz[j]=trie[j][0]=trie[j][1]=0; } cnt=tot=0; for(j=1;j<=n;j++) { if(trie[trie_old[j]][(s[j]>>i)&1]==0) { tot++; trie[trie_old[j]][(s[j]>>i)&1]=tot; } trie_old[j]=trie[trie_old[j]][(s[j]>>i)&1]; siz[trie_old[j]]++; } for(j=1;j<=n;j++) { cnt+=siz[trie[pos[j]][(s[j]>>i)&1]]; } if(k>cnt) { k-=cnt; ans|=(1ull<<i); for(j=1;j<=n;j++) { pos[j]=trie[pos[j]][((s[j]>>i)&1)^1]; } } else { for(j=1;j<=n;j++) { pos[j]=trie[pos[j]][(s[j]>>i)&1]; } } } printf("%lld\n",ans); return 0; }
luogu P5795 [THUSC2015] 异或运算
-
观察到 \(n,p\) 极小,但 \(m\) 极大,对 \(m\) 建出可持久化 \(Trie\) 树,然后暴力枚举 \(n\) 。
-
和上一题一样,从高到低枚举答案的每一位,然后在 \(Trie\) 树上二分。
点击查看代码
ll x[300010],y[300010]; struct PDS_Trie { ll root[300010],rt[300010][2],rt_sum=0; struct Trie { ll ch[2],cnt; }tree[300010*35]; ll build() { rt_sum++; return rt_sum; } void insert(ll pre,ll &rt,ll s) { rt=build(); ll p=rt,q=pre; tree[p].cnt=tree[q].cnt+1; for(ll i=32;i>=0;i--) { for(ll j=0;j<=1;j++) { tree[p].ch[j]=tree[q].ch[j]; } tree[p].ch[(s>>i)&1]=build(); p=tree[p].ch[(s>>i)&1]; q=tree[q].ch[(s>>i)&1]; tree[p].cnt=tree[q].cnt+1; } } ll query(ll u,ll d,ll l,ll r,ll k) { for(ll i=u;i<=d;i++) { rt[i][0]=root[l-1]; rt[i][1]=root[r]; } ll ans=0,cnt; for(ll i=32;i>=0;i--) { cnt=0; for(ll j=u;j<=d;j++) { cnt-=tree[tree[rt[j][0]].ch[(x[j]>>i)&1]].cnt; cnt+=tree[tree[rt[j][1]].ch[(x[j]>>i)&1]].cnt; } if(k>cnt)//所有的都走反方向还不够 { k-=cnt; ans|=(1ll<<i); for(ll j=u;j<=d;j++) { rt[j][0]=tree[rt[j][0]].ch[((x[j]>>i)&1)^1]; rt[j][1]=tree[rt[j][1]].ch[((x[j]>>i)&1)^1]; } } else { for(ll j=u;j<=d;j++) { rt[j][0]=tree[rt[j][0]].ch[(x[j]>>i)&1]; rt[j][1]=tree[rt[j][1]].ch[(x[j]>>i)&1]; } } } return ans; } }T; int main() { ll n,m,p,u,d,l,r,k,i; cin>>n>>m; for(i=1;i<=n;i++) { cin>>x[i]; } for(i=1;i<=m;i++) { cin>>y[i]; T.insert(T.root[i-1],T.root[i],y[i]); } cin>>p; for(i=1;i<=p;i++) { cin>>u>>d>>l>>r>>k; cout<<T.query(u,d,l,r,(d-u+1)*(r-l+1)-k+1)<<endl;//转成第 k 小 } return 0; }
8.14
闲话
- 上午 \(7:30 \sim 11:30\) @KafuuChinocpp 学长安排了一场模拟赛,和 \(DYGX,GYZX\) 的一起打。
- 下午组织讲题,不少整活内容。
- 晚休 \(field\) 查宿。夜聊第一或第二次被 \(field\) 敲门后,先消停了一会儿, @Pursuing_OIer 前去侦查,听见他和 \(field\) 楼道里交谈,但他没关宿舍门,我起来去关门的时候被 \(field\) 贴脸开大, \(field\) 还朝我笑了笑,看见他下楼后我们接着夜聊,然后 \(field\) 就破门而入,说“听那个宿管大姐说你们天天晚上聊到十一二点不睡,早点睡啊”。
做题纪要
Gym103469D Deleting
luogu P7405 [JOI 2021 Final] 雪玉
- 多倍经验: 雪玉 (Snowball)
- 详见 暑假集训CSP提高模拟20 T1 P191. Kanon 。
[ARC157D] YY Garden
8.15
闲话
- 上午 \(7:30 \sim 11:30\) @Muel_imj 学长安排了一场模拟赛,和 \(DYGX\) 的一起打。
- 午休 \(miaomiao\) 查宿。颓《平凡的世界》。
- 下午起床后发现下雨了,雨势还很大,被迫拿了宿舍的一把伞,然后莫名其妙地就把伞套也带上了,塞在了裤右兜里,来机房往外拿酸奶的时候才意识因为兜浅不知道伞套什么时候掉在路上了。讲题前雨就停了,还出太阳了。讲完题上厕所的路上看见自己的伞套被放在了外面窗台了,遂拿走了,
自觉省略一千字感谢小作文。
做题纪要
CF1076D Edge Deletion
CF963B Destruction of a Tree
luogu P10633 BZOJ2989 数列/BZOJ4170 极光
-
增加一维为时间戳,那么操作 \(1\) 等价于单点加。
-
曼哈顿距离直接跑 \(CDQ\) 分治,貌似不太可做,考虑转化为切比雪夫距离。
- 原曼哈顿坐标系中的点 \((x_{1},y_{1}),(x_{2},y_{2})\) 间的距离等价于切比雪夫坐标系中的 \((x_{1}+y_{1},x_{1}-y_{1}),(x_{2}+y_{2},x_{2}-y_{2})\) 。
- 即 \(|x_{1}-x_{2}|+|y_{1}-y_{2}|=\max(|x_{1}+y_{1}-x_{2}-y_{2}|,|x_{1}-y_{1}-x_{2}+y_{2}|)\) ,将左边的绝对值展开并进行归纳即可证明。
-
题意转化为求以 \((x,a_{x})\) 为中心的以 \(2k\) 为边长的正方形内点的个数,同 luogu P4390 [BalkanOI2007] Mokia 摩基亚 二维数点维护即可。
点击查看代码
struct node { int t,x,y,pd,val,id; bool operator < (const node &another) const { return (x==another.x)?((y==another.y)?(pd>another.pd):(y<another.y)):(x<another.x); } }a[500010],tmp[500010]; int ans[500010],y[500010],b[500010],cnt=0,q_cnt=0; void add(int t,int x,int y,int pd,int val,int id) { cnt++; a[cnt].t=t; a[cnt].x=x; a[cnt].y=y; a[cnt].pd=pd; a[cnt].val=val; a[cnt].id=id; } struct BIT { int c[500010]; int lowbit(int x) { return (x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } }T; void cdq(int l,int r,int k) { if(l==r) { return; } int mid=(l+r)/2,x,y; cdq(l,mid,k); cdq(mid+1,r,k); sort(a+l,a+mid+1); sort(a+mid+1,a+r+1); for(x=l,y=mid+1;y<=r;y++) { for(;a[x].x<=a[y].x&&x<=mid;x++) { T.add(k,a[x].y,a[x].pd); } ans[a[y].id]+=a[y].val*T.getsum(a[y].y); } x--; for(int i=l;i<=x;i++) { T.add(k,a[i].y,-a[i].pd); } } int main() { int n,m,x,k,i; string pd; cin>>n>>m; for(i=1;i<=n;i++) { cin>>y[i]; add(0,i+y[i],i-y[i],1,0,0); b[0]++; b[b[0]]=i-y[i]; } for(i=1;i<=m;i++) { cin>>pd; if(pd=="Modify") { cin>>x; cin>>y[x]; add(i,x+y[x],x-y[x],1,0,0); b[0]++; b[b[0]]=x-y[x]; } else { cin>>x>>k; q_cnt++; add(i,x+y[x]+k,x-y[x]+k,0,1,q_cnt); b[0]++; b[b[0]]=x-y[x]+k; add(i,x+y[x]-k-1,x-y[x]+k,0,-1,q_cnt); b[0]++; b[b[0]]=x-y[x]+k; add(i,x+y[x]+k,x-y[x]-k-1,0,-1,q_cnt); b[0]++; b[b[0]]=x-y[x]-k-1; add(i,x+y[x]-k-1,x-y[x]-k-1,0,1,q_cnt); b[0]++; b[b[0]]=x-y[x]-k-1; } } sort(b+1,b+1+b[0]); b[0]=unique(b+1,b+1+b[0])-(b+1); for(i=1;i<=cnt;i++) { a[i].y=lower_bound(b+1,b+1+b[0],a[i].y)-b; } cdq(1,cnt,b[0]); for(i=1;i<=q_cnt;i++) { cout<<ans[i]<<endl; } return 0; }
luogu P5524 [Ynoi2012] NOIP2015 充满了希望
luogu P8844 [传智杯 #4 初赛] 小卡与落叶
-
记上次操作 \(1\) 的深度为 \(lastx\) (初始时设为 \(n+1\) ),则本次操作 \(2\) 等价于查询以 \(x\) 为根的子树内深度 \(\ge lastx\) 的点的数量。
-
\(DFS\) 序排成序列后主席树维护即可。
点击查看代码
struct PDS_SMT { int root[200010],rt_sum=0; struct SegmentTree { int ls,rs,sum; }tree[200010<<5]; #define lson(rt) tree[rt].ls #define rson(rt) tree[rt].rs int build_rt() { rt_sum++; return rt_sum; } void build_tree(int &rt,int l,int r) { rt=build_rt(); tree[rt].sum=0; if(l==r) { return; } int mid=(l+r)/2; build_tree(lson(rt),l,mid); build_tree(rson(rt),mid+1,r); } void update(int pre,int &rt,int l,int r,int pos,int val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum++; if(l==r) { return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos,val); } else { update(rson(pre),rson(rt),mid+1,r,pos,val); } } int query(int rt1,int rt2,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt2].sum-tree[rt1].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt1),lson(rt2),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y); } return ans; } }T; struct node { int nxt,to; }e[200010]; int head[200010],dfn[200010],out[200010],dep[200010],tot=0,cnt=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int fa,int n) { tot++; dfn[x]=tot; dep[x]=dep[fa]+1; T.update(T.root[tot-1],T.root[tot],1,n,dep[x],1); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa) { dfs(e[i].to,x,n); } } out[x]=tot; } int main() { int n,m,u,v,pd,x,lastx,i; cin>>n>>m; lastx=n+1; T.build_tree(T.root[0],1,n); for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs(1,0,n); for(i=1;i<=m;i++) { cin>>pd>>x; if(pd==1) { lastx=x; } else { cout<<T.query(T.root[dfn[x]-1],T.root[out[x]],1,n,lastx,n)<<endl; } } return 0; }
牛客 NC276110 小红的数组重排
-
将 \(\{ a \}\) 升序排序一定是最优情况,难点在怎么 \(check\) 。
-
若存在一个数出现了超过三次或 \(0\) 在第二个位置上(即 \(0\) 出现了两次)则无解。
点击查看代码
int a[500010]; map<int,int>cnt; int main() { int n,flag=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; cnt[a[i]]++; flag|=(cnt[a[i]]>=3); } flag|=(cnt[0]>=2); if(flag==1) { cout<<"NO"<<endl; } else { cout<<"YES"<<endl; sort(a+1,a+1+n); for(i=1;i<=n;i++) { cout<<a[i]<<" "; } } return 0; }
牛客 NC276158 小红的序列乘积2.0
-
设 \(f_{i,j}\) 表示前 \(i\) 个数中序列乘积个位为 \(1\) 的方案数吗,状态转移方程为 \(\begin{cases} f_{i,j \times a_{i} \bmod 10}+=f_{i-1,j} \\ f_{i,j}+=f_{i-1,j} \end{cases}\) 。边界为 \(f_{0,1}=1\) 。
-
最终,有 \(\sum\limits_{i=1}^{n}(f_{i,6}-f_{i-1,6}) \times 2^{n-i}\) 即为所求。
点击查看代码
const ll p=1000000007; ll a[100010],f[100010][12]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } int main() { ll n,ans=0,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } f[0][1]=1; for(i=1;i<=n;i++) { for(j=0;j<=9;j++) { f[i][j*a[i]%10]=(f[i][j*a[i]%10]+f[i-1][j])%p; f[i][j]=(f[i][j]+f[i-1][j])%p; } } for(i=1;i<=n;i++) { ans=(ans+(f[i][6]-f[i-1][6]+p)%p*qpow(2,n-i,p)%p)%p; } cout<<ans<<endl; return 0; }
8.16
闲话
- 上午 \(7:30 \sim 11:30\) \(feifei\) 安排了一场模拟赛,和 \(DYGX,GYZX\) 的一起打。
- 下午临吃晚饭才开始讲题,不少整活内容。下雨仅下了一会儿但雨势很大。
做题纪要
P244. 妄想感伤代偿联盟
luogu P4271 [USACO18FEB] New Barns P
CF1503E 2-Coloring
luogu B4017 [语言月赛 202408] 相识于 2016
luogu B4018 [语言月赛 202408] 游戏与共同语言
luogu B4019 [语言月赛 202408] 皆与生物有缘
luogu B4022 [语言月赛 202408] 蓝色的网易云
luogu B4020 [语言月赛 202408] 两座城市的 543 千米
luogu B4021 [语言月赛 202408] 于抑郁中支持
luogu B4023 [语言月赛 202408] 因友情而终结
luogu B4024 [语言月赛 202408] 保持连接的方式
8.17
闲话
- 上午 \(7:30 \sim 11:30\) @KafuuChinocpp | @H_Kaguya 学长安排了一场模拟赛,和 \(GYZX\) 的一起打。
- 下午讲题。体活的时候 \(feifei\) 只给隔壁机房说了上体活和晚上看电影,我们机房一无所知,所以没去上体活,去吃饭的时候看见隔壁机房都空了才意识到上体活,回来之后才知道晚上有电影。
- 晚上看《当幸福来敲门》,临下课的时候 \(feifei\) 说连着打了 \(4\) 天模拟赛了,所以明天没有模拟赛,让补补近几天的题和补专题。
做题纪要
P266. 军队
2024牛客暑期多校训练营4 J Zero
8.18
闲话
- 早上去食堂 \(6:40\) 才开饭,这么算下来比先前少了 \(3 \sim 4 \min\) 吃饭时间。
- 临吃午饭前 \(huge\) 跟我们说让下午记得拿着伞来,可能要下雨。
- 午休把《平凡的世界》第一部看完了,准备开《平凡的世界》第三部。
- 下午被 \(huge\) 抓了我和 @wkh2008 肩扛椅子面对面, @xrlong 和 @wang54321 互大力敲打对方键盘,于是 \(huge\) 以为我们要打架,“你们是要打架吗?又敲键盘又扛椅子的”。
- 晚休时 \(huge\) 查宿,顺便和他说了我们宿舍进蟑螂的事情(太长时间没值日了)。
做题纪要
2024牛客暑期多校训练营6 I Intersecting Intervals
2024牛客暑期多校训练营4 B Pull the Box
SP13015 CNTPRIME - Counting Primes
-
线筛出所有质数,因为数据水所以珂朵莉树可以通过。
点击查看代码
int a[1000010],prime[1000010],vis[1000010],len=0; void isprime(int n) { memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { break; } } } } struct ODT { struct node { int l,r; mutable int col; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; void init(int n,int a[]) { s.clear(); for(int i=1;i<=n;i++) { s.insert((node){i,i,a[i]}); } } set<node>::iterator split(int pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } int l=it->l,r=it->r,col=it->col; s.erase(it); s.insert((node){l,pos-1,col}); return s.insert((node){pos,r,col}).first; } void assign(int l,int r,int col) { set<node>::iterator itr=split(r+1),itl=split(l); s.erase(itl,itr); s.insert((node){l,r,col}); } int query(int l,int r) { set<node>::iterator itr=split(r+1),itl=split(l); int ans=0; for(set<node>::iterator it=itl;it!=itr;it++) { ans+=(vis[it->col]==0)*(it->r-it->l+1); } return ans; } }O; int main() { int t,n,q,pd,x,y,v,i,j; scanf("%d",&t); isprime(1000000); for(j=1;j<=t;j++) { scanf("%d%d",&n,&q); for(i=1;i<=n;i++) { cin>>a[i]; } O.init(n,a); printf("Case %d:\n",j); for(i=1;i<=q;i++) { scanf("%d%d%d",&pd,&x,&y); if(pd==0) { scanf("%d",&v); O.assign(x,y,v); } else { printf("%d\n",O.query(x,y)); } } } return 0; }
SP19568 PRMQUER - Prime queries
-
单点加转化为区间加,珂朵莉树可以通过同上题。
点击查看代码
int a[100010],prime[10000010],vis[10000010],len=0; void isprime(int n) { memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { break; } } } } struct ODT { struct node { int l,r; mutable int col; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; void init(int n,int a[]) { s.clear(); for(int i=1;i<=n;i++) { s.insert((node){i,i,a[i]}); } } set<node>::iterator split(int pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } int l=it->l,r=it->r,col=it->col; s.erase(it); s.insert((node){l,pos-1,col}); return s.insert((node){pos,r,col}).first; } void assign(int l,int r,int col) { set<node>::iterator itr=split(r+1),itl=split(l); s.erase(itl,itr); s.insert((node){l,r,col}); } void update(int l,int r,int val) { set<node>::iterator itr=split(r+1),itl=split(l); for(set<node>::iterator it=itl;it!=itr;it++) { it->col+=val; } } int query(int l,int r) { set<node>::iterator itr=split(r+1),itl=split(l); int ans=0; for(set<node>::iterator it=itl;it!=itr;it++) { ans+=(it->col<=10000000&&vis[it->col]==0)*(it->r-it->l+1); } return ans; } }O; int main() { int n,m,l,r,v,i; char pd; isprime(10000000); cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } O.init(n,a); for(i=1;i<=m;i++) { cin>>pd; if(pd=='A') { cin>>v>>l; O.update(l,l,v); } if(pd=='R') { cin>>v>>l>>r; O.assign(l,r,v); } if(pd=='Q') { cin>>l>>r; cout<<O.query(l,r)<<endl; } } return 0; }
CF915E Physical Education Lessons
-
区间推平的时候维护下总和来减小常数。因为全是区间推平所以时间复杂度是正确的。
点击查看代码
struct ODT { struct node { int l,r; mutable int col; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; void init(int n) { s.insert((node){1,n,1}); } set<node>::iterator split(int pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } int l=it->l,r=it->r,col=it->col; s.erase(it); s.insert((node){l,pos-1,col}); return s.insert((node){pos,r,col}).first; } void assign(int l,int r,int col) { set<node>::iterator itr=split(r+1),itl=split(l); s.erase(itl,itr); s.insert((node){l,r,col}); } int query(int l,int r) { set<node>::iterator itr=split(r+1),itl=split(l); int ans=0; for(set<node>::iterator it=itl;it!=itr;it++) { ans+=it->col*(it->r-it->l+1); } return ans; } }O; int main() { int n,m,pd,l,r,i; scanf("%d%d",&n,&m); O.init(n); for(i=1;i<=m;i++) { scanf("%d%d%d",&l,&r,&pd); if(pd==1) { O.assign(l,r,0); } else { O.assign(l,r,1); } printf("%d\n",O.query(1,n)); } return 0; }
CF896C Willem, Chtholly and Seniorious
-
区间第 \(k\) 小可以先处理出每种值在这段区间里的出现次数,顺着找即可。
点击查看代码
const ll p=1000000007; ll a[100010]; ll rnd(ll &seed) { ll ret=seed; seed=(seed*7+13)%p; return ret; } ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } struct ODT { struct node { ll l,r; mutable ll col; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; vector<pair<ll,ll> >tmp; void init(ll n,ll a[]) { s.clear(); for(ll i=1;i<=n;i++) { s.insert((node){i,i,a[i]}); } } set<node>::iterator split(ll pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } ll l=it->l,r=it->r,col=it->col; s.erase(it); s.insert((node){l,pos-1,col}); return s.insert((node){pos,r,col}).first; } void assign(ll l,ll r,ll col) { set<node>::iterator itr=split(r+1),itl=split(l); s.erase(itl,itr); s.insert((node){l,r,col}); } void update(ll l,ll r,ll val) { set<node>::iterator itr=split(r+1),itl=split(l); for(set<node>::iterator it=itl;it!=itr;it++) { it->col+=val; } } ll kth_min(ll l,ll r,ll k) { set<node>::iterator itr=split(r+1),itl=split(l); ll ans=0; tmp.clear(); for(set<node>::iterator it=itl;it!=itr;it++) { tmp.push_back(make_pair(it->col,it->r-it->l+1)); } sort(tmp.begin(),tmp.end()); for(ll i=0;i<tmp.size();i++) { if(k>tmp[i].second) { k-=tmp[i].second; } else { ans=tmp[i].first; break; } } return ans; } ll query(ll l,ll r,ll x,ll y) { set<node>::iterator itr=split(r+1),itl=split(l); ll ans=0; for(set<node>::iterator it=itl;it!=itr;it++) { ans=(ans+qpow(it->col%y,x,y)*(it->r-it->l+1)%y)%y; } return ans; } }O; int main() { ll n,m,seed,vmax,pd,l,r,x,y,i; cin>>n>>m>>seed>>vmax; for(i=1;i<=n;i++) { a[i]=rnd(seed)%vmax+1; } O.init(n,a); for(i=1;i<=m;i++) { pd=rnd(seed)%4+1; l=rnd(seed)%n+1; r=rnd(seed)%n+1; if(l>r) { swap(l,r); } if(pd==1) { x=rnd(seed)%vmax+1; O.update(l,r,x); } if(pd==2) { x=rnd(seed)%vmax+1; O.assign(l,r,x); } if(pd==3) { x=rnd(seed)%(r-l+1)+1; cout<<O.kth_min(l,r,x)<<endl; } if(pd==4) { x=rnd(seed)%vmax+1; y=rnd(seed)%vmax+1; cout<<O.query(l,r,x,y)<<endl; } } return 0; }
CF1638E Colorful Operations
-
对于操作 \(2\) ,每个颜色开一个 \(lazy\) 标记记录该元素被加的值。在区间赋值时把原来的贡献加上再减去现在的,基本原理同 luogu P1486 [NOI2004] 郁闷的出纳员 。随便套一个支持区间修改、单点查询的数据结构即可。
-
珂朵莉树的时间复杂度貌似是正确的,但我不会证。
点击查看代码
ll lazy[1000010]; char pd[10]; struct BIT { ll c[1000010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll val) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } }B; struct ODT { struct node { ll l,r; mutable ll col; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; void init(ll n) { s.insert((node){1,n,1}); } set<node>::iterator split(ll pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } ll l=it->l,r=it->r,col=it->col; s.erase(it); s.insert((node){l,pos-1,col}); return s.insert((node){pos,r,col}).first; } void assign(ll l,ll r,ll col,ll n) { set<node>::iterator itr=split(r+1),itl=split(l); for(set<node>::iterator it=itl;it!=itr;it++) { B.add(n,it->l,lazy[it->col]); B.add(n,it->r+1,-lazy[it->col]); } s.erase(itl,itr); s.insert((node){l,r,col}); B.add(n,l,-lazy[col]); B.add(n,r+1,lazy[col]); } ll query(ll pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0}); if(it!=s.end()&&it->l==pos) { return lazy[it->col]+B.getsum(pos); } else { it--; return lazy[it->col]+B.getsum(pos); } } }O; int main() { ll n,m,l,r,c,x,i; scanf("%lld%lld",&n,&m); O.init(n); for(i=1;i<=m;i++) { scanf("%s",pd+1); if(pd[1]=='C') { scanf("%lld%lld%lld",&l,&r,&x); O.assign(l,r,x,n); } if(pd[1]=='A') { scanf("%lld%lld",&c,&x); lazy[c]+=x; } if(pd[1]=='Q') { scanf("%lld",&l); printf("%lld\n",O.query(l)); } } return 0; }
牛客 NC277678 面包店故事
牛客 NC277594 放课后故事
牛客 NC277217 异或故事
牛客 NC277596 构造故事
牛客 NC277601 约会故事
luogu P8512 [Ynoi Easy Round 2021] TEST_152
-
同 luogu P5524 [Ynoi2012] NOIP2015 充满了希望 ,询问时扫描线维护,树状数组维护时间戳即可。
-
区间推平考虑珂朵莉树,推平时类似 CF915E Physical Education Lessons | CF1638E Colorful Operations 维护下全局和即可。
点击查看代码
ll l[500010],r[500010],val[500010],ans[500010]; vector<pair<ll,ll> >e[500010]; struct BIT { ll c[500010]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll val) { if(x!=0) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } } } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } }B; struct ODT { struct node { ll l,r; mutable ll col,t; bool operator < (const node &another) const { return l<another.l; } }; set<node>s; void init(ll n) { s.insert((node){1,n,0,0}); } set<node>::iterator split(ll pos) { set<node>::iterator it=s.lower_bound((node){pos,0,0,0}); if(it!=s.end()&&it->l==pos) { return it; } it--; if(it->r<pos) { return s.end(); } ll l=it->l,r=it->r,col=it->col,t=it->t; s.erase(it); s.insert((node){l,pos-1,col,t}); return s.insert((node){pos,r,col,t}).first; } void assign(ll l,ll r,ll col,ll n,ll t) { set<node>::iterator itr=split(r+1),itl=split(l); for(set<node>::iterator it=itl;it!=itr;it++) { B.add(n,it->t,-(it->col*(it->r-it->l+1))); } s.erase(itl,itr); s.insert((node){l,r,col,t}); B.add(n,t,col*(r-l+1)); } }O; int main() { ll n,m,q,x,y,i,j; cin>>n>>m>>q; O.init(m); for(i=1;i<=n;i++) { cin>>l[i]>>r[i]>>val[i]; } for(i=1;i<=q;i++) { cin>>x>>y; e[y].push_back(make_pair(x,i)); } for(i=1;i<=n;i++) { O.assign(l[i],r[i],val[i],n,i); for(j=0;j<e[i].size();j++) { ans[e[i][j].second]=B.getsum(i)-B.getsum(e[i][j].first-1); } } for(i=1;i<=q;i++) { cout<<ans[i]<<endl; } return 0; }
8.19
闲话
- 上午 \(7:30 \sim 11:30\) \(miaomiao\) 安排了一场模拟赛,和 \(DYGX,GYZX\) 的一起打。
- 临吃午饭的时候给 \(miaomiao\) 说了头顶空调的冷凝管还在漏水的事情。
- 午休颓《平凡的世界》。
- 晚上 \(feifei\) 跟我们说以后模拟赛改成了 \(7:00\) 开始。
做题纪要
[ABC238D] AND and SUM
[ABC366F] Maximum Composition
luogu P5999 [CEOI2016] kangaroo
牛客 NC277160 不是烤串故事
CF464E The Classic Problem
8.20
闲话
- 上午 \(7:30 \sim 11:30\) @KafuuChinocpp | @H_Kaguya 学长安排了一场模拟赛,和 \(DYGX,GYZX\) 的一起打。
- 午休颓《平凡的世界》。
- 下午 @Charlie_ljk 回来了,并告诉我们 \(feifei\) 给他说 \(8.25\) 下午放假了。
- 晚休的时候 \(field\) 查宿,遂 @Pursuing_OIer 给他说了我们宿舍需要杀虫剂的事情,然后 \(field\) 就去学校门口的小卖部去买了并送到了我们宿舍,走的时候还不忘提醒我们等明天早上人都走了再喷,他可不想明天就见不到我们了。
做题纪要
luogu U259681 败者食尘(加强版)
2024牛客暑期多校训练营6 D Puzzle: Wagiri
[ABC276G] Count Sequences
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18353678,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。