杭电多校杂题收录

0|1前言

和学长学弟一起打的hdu多校,打的很菜没啥难题收录,因为难的我都不会做。


1|0正题


1|1hdu7152-Copy

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7152

1|0题目大意

n个数字的序列am次操作,每次将一段[l,r]复制一份然后插入在这一段的后面,或者求某个位置的值。

li,rin

1|2解题思路

复制之后相当于让大于ri的位置询问时都减去rili+1这一段编号,也就是我们询问x时倒序枚举目前的询问,对于每个询问如果x>ri那么令x=xrili+1

但是询问可能很多,我们考虑暴力重构,值保留最后q个操作,然后询问时暴力倒序枚举。每到达q个操作后我们就暴力重新构造一次序列。

重构的时候我们就一堆位置一起处理,每次倒序之后每次操作[li,ri]相当于把[ri+1,ri+rili+1]这个区间和[li,ri]绑定,我们把这个区间删除,然后用并查集指向[li,ri]

然后删除的做法,我们可以标记一下删除,然后查询位置的时候用树状数组查询即可。

1|0code

#include<cstdio> #include<cstring> #include<algorithm> #define lowbit(x) (x&-x) using namespace std; const int N=1e5+10; int T,n,q,k,ans,a[N],l[N],r[N],fa[N],t[N],f[N]; int find(int x) {return (fa[x]==x)?(x):(fa[x]=find(fa[x]));} void Change(int x,int val){ while(x<=n+1){ t[x]+=val; x+=lowbit(x); } return; } int Ask(int k){ int x=0;k--; for(int i=17;i>=0;i--) if(x+(1<<i)<=n+1&&t[x+(1<<i)]<=k)x+=1<<i,k-=t[x]; return x+1; } int main() { scanf("%d",&T); while(T--){ scanf("%d%d",&n,&q); k=ans=0; for(int i=1;i<=n;i++) scanf("%d",&a[i]); while(q--){ int op;scanf("%d",&op); if(op==1){ k++; scanf("%d%d",&l[k],&r[k]); if(k>=400){ for(int i=1;i<=n+1;i++)t[i]=0,fa[i]=i; for(int i=1;i<=n+1;i++)Change(i,1); int m=n; for(int i=k;i>=1;i--){ for(int j=0,p;j<=r[i]-l[i];j++){ p=Ask(r[i]+1); if(p>n)break; fa[p]=Ask(l[i]+j); Change(p,-1);m--; } } for(int i=1;i<=m;i++)f[Ask(i)]=a[i]; for(int i=1;i<=n;i++)a[i]=f[find(i)]; k=0; } } else{ int x;scanf("%d",&x); for(int i=k;i>=1;i--) if(x>r[i])x-=r[i]-l[i]+1; ans^=a[x]; } } printf("%d\n",ans); } return 0; }

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7184

1|0题目大意

给出n个数字的一个序列,你每次可以选择一个区间将其变为它们所有数的异或和,求将所有数字变成同一个数字,最大化这个数字。

保证有两个相同数字。

1|0解题思路

考虑一个大于0的区间我们都可以将其变为0,并且0不影响操作,我们可以无视。

而如果有两个数字x,y之间相隔1(中间设为z)但是我们都要保留,那么此时我们可以用那两个相同的数字a,我们将它们变为x,z,y xor a,y xor a

然后再异或一次左边两个得到x,z xor a xor y,z xor a xor y,y xor a

然后异或出x,z xor a xor y,z,z,再异或得到x,a xor y,z,z,再用另一个ax异或掉即可。

所以就算求最大异或和,线性基就行了。


1|4hdu7190-BBQ

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7190

1|0题目大意

一个字符串s,每次可以插入/删除/修改一个字符,求至少操作多少次满足:

  • s的长度为4的倍数
  • s4i+1=s4i+4s4i+2=s4i+3

1|0解题思路

我们考虑一下dp,看下能不能将复杂度压进26n以内。

一个一个字符考虑,然后dp

  • f0i表示目前确定了第1个字符,为i的方案。
  • f1i,j表示目前确定了前两个字符,分别是i,j的方案。
  • f2i表示目前确定第三个字符,第一个是i的方案。
  • f4表示目前确定了四个字符的方案。

会发现瓶颈在f1i,j的转移上,但是我们假设我们目前枚举到p,会发现每次只需要转移j=ap的方案,所以这部分转移也很好做,我们默认每个字符都会被删除,然后当一个字符不会被删除时会产生1的贡献。

至于插入和修改,因为它们可以是任意字符,我们再开一个状态26表示可以是任意字符就好了。

1|0code

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=27; int T,n,f0[N],f1[N][N],f2[N],g[N],f3; char s[1100000]; int Min(int x,int y) {return (x<y)?x:y;} int main() { scanf("%d",&T); while(T--){ scanf("%s",s+1); n=strlen(s+1); // n=1e6;for(int i=1;i<=n;i++)s[i]='a'; int mi=0; memset(f0,0x3f,sizeof(f0)); memset(f1,0x3f,sizeof(f1)); memset(f2,0x3f,sizeof(f2)); memset(g,0x3f,sizeof(g)); f3=0; f0[26]=Min(f0[26],f3+1); for(int j=0;j<27;j++)f1[j][26]=Min(f1[j][26],f0[j]+1),g[j]=Min(g[j],f1[j][26]); for(int j=0;j<27;j++)f2[j]=Min(f2[j],g[j]+1); for(int j=0;j<27;j++)f3=Min(f3,f2[j]+1); for(int i=1;i<=n;i++){ f0[26]=Min(f0[26],f3+1); for(int j=0;j<27;j++)f1[j][26]=Min(f1[j][26],f0[j]+1),g[j]=Min(g[j],f1[j][26]); for(int j=0;j<27;j++)f2[j]=Min(f2[j],g[j]+1); for(int j=0;j<27;j++)f3=Min(f3,f2[j]+1); int c=s[i]-'a',p3=f3; f3=Min(f3,Min(f2[c],f2[26])-1); for(int j=0;j<26;j++)f3=Min(f3,f2[j]); for(int j=0;j<27;j++){ f2[j]=Min(f2[j],Min(f1[j][c],f1[j][26])-1); f2[j]=Min(f2[j],g[j]); } for(int j=0;j<27;j++){ f1[j][c]=Min(f1[j][c],f0[j]-1),g[j]=Min(g[j],f1[j][c]); f1[j][26]=Min(f1[j][26],f0[j]),g[j]=Min(g[j],f1[j][26]); } f0[c]=Min(f0[c],p3-1); f0[26]=Min(f0[26],Min(p3,f3+1)); for(int j=0;j<27;j++)f1[j][26]=Min(f1[j][26],f0[j]+1),g[j]=Min(g[j],f1[j][26]); for(int j=0;j<27;j++)f2[j]=Min(f2[j],g[j]+1); for(int j=0;j<27;j++)f3=Min(f3,f2[j]+1); } printf("%d\n",f3+n); } return 0; }

1|5hdu7206-Planar graph

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7206

1|0题目大意

给出一张平面图,求最少删掉多少条边使得其对偶图联通,输出字典序最小的方案。

1|0解题思路

有环的话对偶图就不连通,所以删的只剩下一棵生成树就好了。


1|6hdu7207-Find different

单独写了:https://blog.csdn.net/Mr_wuyongcong/article/details/126329092


1|7hdu7217-Counting Good Arrays

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7217

1|0题目大意

求有多少个长度不超过n且值域为[1,m]的序列满足每一个数都是下一个数的因数。

1|0解题思路

我们反过来填,每个数字后面只能填它的倍数,那么如果第一个数是a1a2就只有ma1个数字可以填。
我们考虑如果每次至少加一个因数,那么这个序列长度不超过logm,然后我们再用组合数把这些加因数的位置分配到n里面就行了。
fi,j表示mal=i,且增加了j次因数时的答案,这样i可以通过整除分块得到n级别种取值,转移时也是整除分块去考虑加的因数就好了。
时间复杂度:O(m34logm)

有不带logmin25筛但是我不会。

1|0code

#pragma GCC optimize(2) %:pragma GCC optimize(3) %:pragma GCC optimize("Ofast") %:pragma GCC optimize("inline") #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define ll long long using namespace std; const int N=1e5+10,P=1e9+7; int cas,n,m,inv[70],a[N],p1[N],p2[N],f[N][30],c[60]; void Add(int &x,int y) {x=(x+y>=P)?(x+y-P):(x+y);} void Add(int &x,ll y) {x=(x+y>=P)?(x+y-P):(x+y);} int C(int n,int m){ if(m>n)return 0;int ans=1; for(int i=0;i<m;i++)ans=1ll*ans*(n-i)%P; for(int i=1;i<=m;i++)ans=1ll*ans*inv[i]%P; return ans; } int main() { inv[1]=1; for(int i=2;i<=60;i++)inv[i]=P-1ll*inv[P%i]*(P/i)%P; scanf("%d",&cas); while(cas--){ scanf("%d%d",&m,&n); int lim=0,ans=0; while((1<<lim)<=n)lim++,c[lim]=C(m,lim); int T=sqrt(n),cnt=0; for(int l=1,r;l<=n;l=r+1){ r=n/(n/l);a[++cnt]=n/l; if(n/l<=T)p1[n/l]=cnt; else p2[n/(n/l)]=cnt; f[cnt][0]=r-l+1; for(int i=1;i<lim;i++)f[cnt][i]=0; } f[1][0]=1; for(int i=1;i<=cnt;i++){ int x=a[i],y; for(int l=2,r;l<=x;l=r+1){ r=x/(x/l); if(x/l<=T)y=p1[x/l]; else y=p2[n/(x/l)]; for(int k=0;k<lim-1;k++) Add(f[y][k+1],1ll*f[i][k]*(r-l+1)%P); } for(int j=0;j<lim;j++) Add(ans,1ll*c[j+1]*f[i][j]%P); } printf("%d\n",ans); } return 0; }

1|8hdu7221-Darkmoon Faire

http://acm.hdu.edu.cn/showproblem.php?pid=7221

1|0题目大意

有一个长度为n的序列,求将它分割成若干连续段的方案满足:

  • 将每一段单独取出来后,最大值在奇数位置,最小值在偶数位置。

1|0解题思路

一个朴素的想法是我们去搞两个单调栈去维护最小最大值,然后以这两个单调栈里的数去分割成若干个区间,每个区间产生贡献的方式不同。

但是我们发现一个单调栈改变时可能对应另一个单调栈里的多个区间,所以不能暴力修改,我们用线段树维护每个单调栈里(不考虑令一个单调栈)产生贡献的数字奇数和偶数位置的和,然后修改时查询即可。

1|0code

#include<cstdio> #include<cstring> #include<algorithm> #include<stack> #define ll long long using namespace std; const ll N=3e5+10,P=998244353; ll cas,n,a[N];stack<int> s,t; struct SegTree{ ll w[N<<2][2][2],lazy[N<<2]; void Clear(){ memset(w,0,sizeof(w)); memset(lazy,0,sizeof(lazy)); } void Downdata(ll x){ if(!lazy[x])return; swap(w[x*2][0],w[x*2][1]); swap(w[x*2+1][0],w[x*2+1][1]); lazy[x*2]^=1;lazy[x*2+1]^=1;lazy[x]=0;return; } void Ins(ll x,ll L,ll R,ll pos,ll val){ if(L==R){w[x][0][pos&1]=val;return;} ll mid=(L+R)>>1;Downdata(x); if(pos<=mid)Ins(x*2,L,mid,pos,val); else Ins(x*2+1,mid+1,R,pos,val); w[x][0][0]=(w[x*2][0][0]+w[x*2+1][0][0])%P; w[x][1][0]=(w[x*2][1][0]+w[x*2+1][1][0])%P; w[x][0][1]=(w[x*2][0][1]+w[x*2+1][0][1])%P; w[x][1][1]=(w[x*2][1][1]+w[x*2+1][1][1])%P; } void Change(ll x,ll L,ll R,ll l,ll r){ if(L==l&&R==r){lazy[x]^=1;swap(w[x][0],w[x][1]);return;} ll mid=(L+R)>>1;Downdata(x); if(r<=mid)Change(x*2,L,mid,l,r); else if(l>mid)Change(x*2+1,mid+1,R,l,r); else Change(x*2,L,mid,l,mid),Change(x*2+1,mid+1,R,mid+1,r); w[x][0][0]=(w[x*2][0][0]+w[x*2+1][0][0])%P; w[x][1][0]=(w[x*2][1][0]+w[x*2+1][1][0])%P; w[x][0][1]=(w[x*2][0][1]+w[x*2+1][0][1])%P; w[x][1][1]=(w[x*2][1][1]+w[x*2+1][1][1])%P; } ll Ask(ll x,ll L,ll R,ll l,ll r,ll k){ if(L==l&&R==r)return w[x][1][k]; ll mid=(L+R)>>1;Downdata(x); if(r<=mid)return Ask(x*2,L,mid,l,r,k); if(l>mid)return Ask(x*2+1,mid+1,R,l,r,k); return (Ask(x*2,L,mid,l,mid,k)+Ask(x*2+1,mid+1,R,mid+1,r,k))%P; } }T[2]; signed main() { scanf("%lld",&cas); while(cas--){ while(!s.empty())s.pop();T[0].Clear(); while(!t.empty())t.pop();T[1].Clear(); scanf("%lld",&n); ll sum=0;s.push(0);t.push(0); T[0].Ins(1,1,n,1,1);T[1].Ins(1,1,n,1,1);T[1].Change(1,1,n,1,1); for(ll i=1;i<=n;i++){ scanf("%lld",&a[i]); while(s.size()>1&&a[i]<a[s.top()]){ ll x=s.top();s.pop(); (sum-=T[1].Ask(1,1,n,s.top()+1,x,!(x&1)))%=P; if((x-i)&1)T[0].Change(1,1,n,s.top()+1,x); (sum+=T[1].Ask(1,1,n,s.top()+1,x,!(i&1)))%=P; } while(t.size()>1&&a[i]>a[t.top()]){ ll x=t.top();t.pop(); (sum-=T[0].Ask(1,1,n,t.top()+1,x,x&1))%=P; if((x-i)&1)T[1].Change(1,1,n,t.top()+1,x); (sum+=T[0].Ask(1,1,n,t.top()+1,x,i&1))%=P; } if(i<n){ T[0].Ins(1,1,n,i+1,sum); T[1].Ins(1,1,n,i+1,sum); T[1].Change(1,1,n,i+1,i+1); } s.push(i);t.push(i); } printf("%lld\n",(sum+P)%P); } return 0; }

1|9hdu7224-Ironforge

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7224

1|0题目大意

一个n段的数轴,第i个点上有数字ai,通过(i,i+1)要求存在之前经过的某个ax满足bi|ax

q次询问x能否到达y

1|0解题思路

一个点能到达的点肯定是一个区间,暴力的想法是左右两边扩展。实际上暴力加上一个记忆化就能过。

我们假设x能到达yx+1能到达y但是x+1不能到达x,那么此时x+1y这段路上bi的因数x+1x都有,但是bx这个因数x+1没有x有,也就是xx+1多一个因数。

所以这种情况不会往左出现很多,因为x需要不停增大,所以我们从左往右暴力,一个点暴力时如果能到达它左边的点就让它能到达的区间并上左边那个点能到达的区间即可。

时间复杂度:O((q+n)logn)


1|10hdu7226-Darnassus

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7226

1|0题目大意

有一个排列pxy之间的边权权值为|xy|×|pxpy|,求最小生成树。

1|0解题思路

因为p是排列,会发现合并两个连通块的代价不超过n1(连接最近的两个)。

所以我们直接删除代价大于n1的边即可,那么剩下的对数满足min{|ij|,|pipj|}n,所以暴力枚举n以内的对然后记下每一条合法的边跑最小生成树。

因为边权不超过n所以可以直接桶排。

时间复杂度:O(nnα(n))


1|11hdu7232-Shattrath City

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=7232

1|0题目大意

求有多少个长度为m,值域为n的序列满足每一个长度为n的连续段都不是一个1n的排列。

1|0解题思路

考虑容斥,钦定一些区间是1n的排列,那么如果相邻位置x,y两个不超过n贡献是(yx)!,否则是n!×nyx

分治NTT或者多项式求逆计算即可。


__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/16585155.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(66)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2021-08-14 Loj#2880-「JOISC 2014 Day3」稻草人【CDQ分治,单调栈,二分】
点击右上角即可分享
微信分享提示