wzOI 2023.8.31 题解
2023-09-01 15:59:41
太菜了,第一题都打成那样了没发现是 MST,第三题数据范围没有很仔细看,以为是乱搞(组合之类的)题就直接跳了。
不得不说这次比赛题目的一些思路还是挺妙的,一些想法确实我这种成分想不太到。
给出了
首先两个特殊情况的
发现我们需要且只需要
int n,m; namespace subtask1{ bool f[15][15],choose[15]; struct Que{ int l,r,c; }q[25]; queue<PII>Q; bool check(){//判断这些边是否可行 while(!Q.empty()){ int l=Q.front().first,r=Q.front().second; Q.pop(); for(int i=l;i<=r;i++){ if(i<r&&(!f[i+1][r])&&f[l][i]&&f[l][r]){ f[i+1][r]=1; Q.push({i+1,r}); } if(i>l&&(!f[l][i-1])&&f[l][r]&&f[i][r]){ f[l][i-1]=1; Q.push({l,i-1}); } } for(int i=r+1;i<=n;i++){ if((!f[r+1][i])&&f[l][r]&&f[l][i]){ f[r+1][i]=1; Q.push({r+1,i}); } } for(int i=l-1;i;--i){ if((!f[i][l-1])&&f[i][r]&&f[l][r]){ f[i][l-1]=1; Q.push({i,l-1}); } } } for(int i=1;i<=n;i++) if(!f[i][i])return 0; return 1; } bool work(){ if(m<n)return puts("Genshin Impact, Qi Dong!"),0; for(int i=1;i<=m;i++){ int l=read(),r=read(),c=read();//记录一下所有边 q[i]={l,r,c}; } for(int i=1;i<=n;i++)choose[i]=1;//n个条件足矣 reverse(choose+1,choose+1+m);//预处理permutation 数组 ll ans=1e18; do{ ll res=0; memset(f,0,sizeof(f)); for(int i=1;i<=m;i++){ if(choose[i])f[q[i].l][q[i].r]=1,res+=q[i].c,Q.push({q[i].l,q[i].r});//标记并处理队列 } if(check()){//合理就记录 ans=min(ans,res); } }while(next_permutation(choose+1,choose+1+m)); if(ans>=1e17)puts("Genshin Impact, Qi Dong!"); else cout<<ans; return 0; } } int f[5003][5003]; queue<PII>Q; int main(){ freopen("freopen.in","r",stdin); freopen("freopen.out","w",stdout); n=read(),m=read(); if(n<=5)return subtask1::work(); memset(f,0x3f,sizeof(f)); for(int i=1;i<=m;i++){ int l=read(),r=read(),c=read(); f[l][r]=min(f[l][r],c); } ll ans=0; for(int i=1;i<=n;i++){ if(f[i][i]>1e9) return puts("Genshin Impact, Qi Dong!"),0; ans+=f[i][i]; } cout<<ans; return 0; }
不难发现
为什么
typedef pair<int,int> PII; int n,m,dis[5003],tot,ans; vector<PII>f[5003]; bool vis[5003]; priority_queue<PII>Q; int main(){ n=read(),m=read(); for(int i=1,u,v,w;i<=m;i++) f[u=read()-1].push_back({v=read(),w=read()}),f[v].push_back({u,w}); memset(dis,0x3f,sizeof(dis)); Q.push({dis[1]=0,1}); while(!Q.empty()&&tot<=n){ int x=Q.top().second; Q.pop(); if(vis[x])continue; vis[x]=1;tot++; ans+=dis[x]; for(PII PI:f[x]){ int y=PI.first,w=PI.second; if(dis[y]>w)Q.push({-(dis[y]=w),y}); } } if(tot<=n)puts("Genshin Impact, Qi Dong!"); else cout<<ans; return 0; }
给你
, ,其中 。 。
求
先讲一个非常愚蠢的暴力,忘记了质数表示的数不会重新重复的情况,所以甚至用了一个 unordered_set
去重,然后果不其然只拿了 30 分就 T 了。
//30pts int n; ll R,a[35],maxx; unordered_set<ll>S; void dfs(ll x){ S.insert(x); maxx=max(maxx,x); for(int i=1;i<=n;i++){ if(x*a[i]>R)break; if(S.find(x*a[i])!=S.end())continue; dfs(x*a[i]); } } int main(){ cin>>n>>R; for(int i=1;i<=n;i++){ cin>>a[i]; } sort(a+1,a+1+n); dfs(1); printf("%lld\n%lld",maxx,S.size()); return 0; }
比较智慧(更接近正解)的暴力,枚举每个质数乘了几次,复杂度为
//70pts void dfs(ll x,int w){ if(w>n){ maxx=max(maxx,x); cnt++; return; } while(x<=R){ dfs(x,w+1); x*=a[w]; } } //dfs(1,1);
正解基本上也是暴力,只不过用了一种比较智慧的方法,使得复杂度比答案要小不少。
基本思想:
现在关键在于我们怎样才能让复杂度不是答案,不能盲目枚举。
考虑把原素数分成合适大小的两组分开搜索,得到数集
int n; ll R,a[26],ans,cnt; vector<ll>f; void dfs1(ll x,int t){ if(t>8||t>n)return f.push_back(x),void();//这里不对数量统计是因为下面当值为 1 时会统计一次。 while(1){ dfs1(x,t+1); if(x>R/a[t])break;//防止用乘法爆long long导致死循环 x*=a[t]; } } void dfs2(ll x,int t){ if(t>n){ auto w=--upper_bound(f.begin(),f.end(),R/x);//找大于这个值的第一个,然后减一就是第一个小于等于的,保证不会是 f.end() cnt+=w-f.begin()+1; ans=max(ans,x*(*w)); return; } while(1){ dfs2(x,t+1); if(x>R/a[t])break; x*=a[t]; } } int main(){ cin>>n>>R; for(int i=1;i<=n;i++)cin>>a[i]; sort(a+1,a+1+n); dfs1(1,1); sort(f.begin(),f.end());//排序保证了后面的二分 dfs2(1,9);//小组放8个 cout<<ans<<endl<<cnt; return 0; }
答案对
看到范围和无脑方案数,想到矩阵快速幂。
简单的想个转移:
令
那么显然有:
吐槽:这长得都像个矩阵乘法!!!!
因为
int n,m,q; const int mod=2333;//模数太小,甚至不需要开 long long struct Matrix{ int a[41][41]; Matrix(){memset(a,0,sizeof(a));} void build(){ for(int i=1,u,v;i<=m;i++) a[u=read()][v=read()]=1,a[v][u]=1; } void init(){ for(int i=1;i<=n;i++)a[i][i]=1; } Matrix operator *(const Matrix &w)const{ Matrix ans; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) (ans.a[i][j]+=a[i][k]*w.a[k][j]%mod)%=mod; return ans; } }ori; Matrix qpow(Matrix x,int w){ Matrix res; for(res.init();w;w>>=1,x=x*x)if(w&1)res=res*x; return res; } int main(){ n=read(),m=read(); ori.build(); q=read(); while(q--){ int st=read(),en=read(),L=read(),R=read(); Matrix res=qpow(ori,L); int ans=0; for(int i=L;i<=R;i++){ (ans+=res.a[st][en])%=mod; res=res*ori; } printf("%d\n",ans); } return 0; }
给定一个长度为
求区间长度为区间和倍数的子区间个数。
即求:
暴力太好打了,不说了。
因为这题是 jca 本鸡出的,那么枚举一下算法:数学,根号,数据结构。
再看一眼数据范围:
长得就不像数据结构,如果出数学那肯定会开到
赛后发现解法非常有趣。
所以有枚举
设阈值为
当
于是,我们可以枚举
我们枚举
注意:
ll ans; queue<int>Q; int n,sum[100005],sq,buc[45000005]; int main(){ sq=sqrt(n=read()); for(int i=1;i<=n;i++)sum[i]=sum[i-1]+read(); for(int k=1;k<=sq;k++){//用 unordered_map T 了所以换 queue for(int i=0;i<=n;i++)buc[sum[i]*k-i+n]++,Q.push(sum[i]*k-i+n);//+n 防止负下标 while(!Q.empty())ans+=1ll*buc[Q.front()]*(buc[Q.front()]-1)>>1,buc[Q.front()]=0,Q.pop();//有重没关系 } for(int t=1;t<=n/sq;t++){//枚举 t int L=0,R=0;//双指针,因为 r 区间肯定是不会变小的 for(int l=0;l<n;l++){//枚举 l-1 while(sum[L]-sum[l]<t&&L<=n)L++; if(L>n)break; while(sum[R]-sum[l]<=t&&R<=n)R++;//[L,R) int ll=max(L-l,t*sq+1),rr=R-1-l;//代入公式 if(ll<=rr)ans+=rr/t-(ll-1)/t;//判一下,计算贡献 } } cout<<ans; return 0; }
本文作者:NBest
本文链接:https://www.cnblogs.com/NBest/p/17687002.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步