半生风雪

NOIP模拟84(多校17)

Varuxn·2021-10-27 21:01·148 次阅读

NOIP模拟84(多校17)

T1 宝藏

解题思路#

考场上一眼出 nlog2 做法,然后没看见是 1s 3e5 的数据,我竟然以为自己切了??

考完之后尝试着把二分改为指针的移动,然后就过了??或许是数据水吧,感觉自己的做法指针好像并不满足单调性。。

口胡一下正解,做法差不多,只不过枚举的方式改变了,但是都需要先对于 w 进行排序,枚举每一种长度的序列,单调指针维护最大的合法的值。

这个是有单调性的,然后主席树或者权值线段树维护均可。

code#

其实是假做法

Copy
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"RP++"<<endl #define ls x<<1 #define rs x<<1|1 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=3e5+10,M=1e6+10; int n,m,q,lim,ans[N]; struct Node{int w,t;}s[N]; struct Segment_Tree { int root,all; struct node{int siz,dat;}tre[M<<2]; #define push_up(x) tre[x].dat=tre[ls].dat+tre[rs].dat,tre[x].siz=tre[ls].siz+tre[rs].siz void insert(int x,int l,int r,int pos,int val) { if(l==r) return tre[x].siz+=val,tre[x].dat+=val*pos,void(); int mid=(l+r)>>1; if(pos<=mid) insert(ls,l,mid,pos,val); else insert(rs,mid+1,r,pos,val); push_up(x); } int query(int x,int l,int r,int k) { if(!tre[x].siz||!k) return 0; if(l==r) return tre[x].dat*k/tre[x].siz; int mid=(l+r)>>1; if(tre[ls].siz>k) return query(ls,l,mid,k); return tre[ls].dat+query(rs,mid+1,r,k-tre[ls].siz); } }T1,T2; bool comp(Node x,Node y){return x.w<y.w;}; #undef int int main() { #define int long long freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout); n=read(); m=read(); q=read(); memset(ans,-1,sizeof(ans)); for(int i=1;i<=n;i++) s[i].w=read(),s[i].t=read(),lim=max(lim,s[i].t); sort(s+1,s+n+1,comp); for(int i=1;i<=n;i++) T2.insert(1,0,lim,s[i].t,1); for(int i=1,val=0;i<=n;i++) { T2.insert(1,0,lim,s[i].t,-1); val=min(val,min(i-1,n-i)); while(val<min(i-1,n-i)&&T1.query(1,1,lim,val+1)+T2.query(1,1,lim,val+1)+s[i].t<=m) val++; while(val>0&&T1.query(1,1,lim,val)+T2.query(1,1,lim,val)+s[i].t>m) val--; ans[val]=max(ans[val],s[i].w); T1.insert(1,0,lim,s[i].t,1); } for(int i=n/2;i>=0;i--) ans[i]=max(ans[i],ans[i+1]); while(q--){int x;x=read();printf("%lld\n",ans[min(n,x/2)]);} return 0; }

T2 寻找道路

解题思路#

首先考虑去除前导 0 的影响,直接搜索一遍查找所有到 1 节点距离为 0 的点记录下来就好了。

剩下的部分就是字典序以及长度的问题了,那么长度的问题直接 BFS 就可以了。

字典序大小的话,对于队列中长度一致并且数字序列相同的一起拿出来,然后优先扫 0 边权的边再扫 1 边权的边。

标记一下,保证每个节点只访问一次。

code#

Copy
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"RP++"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=1e6+10,mod=1e9+7,INF=1e18; int n,m,top,sta[N],len[N],dis[N]; int tot=1,head[N],ver[N<<1],nxt[N<<1],edge[N<<1]; vector<int> dis0; queue<int> q; bool vis[N]; void add_edge(int x,int y,int val) { ver[++tot]=y; edge[tot]=val; nxt[tot]=head[x]; head[x]=tot; } #undef int int main() { #define int long long freopen("path.in","r",stdin); freopen("path.out","w",stdout); n=read(); m=read(); memset(len,0x3f,sizeof(len)); dis[1]=len[1]=0; for(int i=1,x,y,z;i<=m;i++) x=read(),y=read(),z=read(),add_edge(x,y,z); q.push(1); dis0.push_back(1); while(!q.empty()) { int x=q.front(); q.pop(); for(int i=head[x];i;i=nxt[i]) if(!vis[ver[i]]&&!edge[i]) vis[ver[i]]=true,dis0.push_back(ver[i]),q.push(ver[i]); } for(auto it:dis0) q.push(it),dis[it]=len[it]=0; while(!q.empty()) { top=0; sta[++top]=q.front(); q.pop(); while(!q.empty()&&len[q.front()]==len[sta[1]]&&dis[q.front()]==dis[sta[1]]) sta[++top]=q.front(),q.pop(); for(int i=1;i<=top;i++) { int x=sta[i]; for(int j=head[x];j;j=nxt[j]) { int to=ver[j],val=edge[j]; if(val||vis[to]||len[to]<=len[x]+1) continue; len[to]=len[x]+1; dis[to]=dis[x]*2%mod; vis[to]=true; q.push(to); } } for(int i=1;i<=top;i++) { int x=sta[i]; for(int j=head[x];j;j=nxt[j]) { int to=ver[j],val=edge[j]; if(!val||vis[to]||len[to]<=len[x]+1) continue; len[to]=len[x]+1; dis[to]=(dis[x]*2+1)%mod; vis[to]=true; q.push(to); } } } for(int i=2;i<=n;i++) printf("%lld ",len[i]>=INF?-1ll:dis[i]); return 0; }

T3 猪国杀

解题思路#

其实是个假期望,计数 DP 。

我们只需要知道每一种方案的总和了,最后乘上一个 An

gi,j,k 表示有 多少个⻓度为 i 的正整数序列满足每一个数字不大于 j 且所有数字总和不超过 k

假设我们能够求出来这个值,考虑如何计算答案。

枚举选的牌中的最大值 j ,最大值个数 k ,以及选了 i 个小于 j 的牌,于是就有了:

i=0nj=1Ak=1nigi,j1,mj×k×(ni)t=kni(nit)×(Aj)nit

对于计算过方案数的两个序列就可以视为序列中的元素是等价的了,也就是再乘上一个可重集排列。

因为我们要选择 kj 但是序列中不一定只有 kj 因此我们需要让前面的牌的总和是 mj×k 然后枚举后面有多少个 j 同时计算剩下的取值的个数。

对于 gi,j,k 可以通过枚举多少个大于 j 的数字进行计算,可以运用挡板法,由于挡板之间 1 的个数可能会超过 j 因此需要容斥一下:

gi,j,k=t=0i(1)t(it)(kt×ji)

code#

Copy
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"RP++"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=110,M=1010,mod=998244353; int n,m,lim,mx,ans,fac[M],ifac[M]; void add(int &x,int y){x+=y;if(x>=mod)x-=mod;} int power(int x,int y,int p=mod) { int temp=1; while(y) { if(y&1) temp=temp*x%p; x=x*x%p; y>>=1; } return temp; } int C(int x,int y){return x<y?0:fac[x]*ifac[y]%mod*ifac[x-y]%mod;} int g(int i,int j,int k) { int sum=0; for(int p=0,sym=1;p<=i;p++,sym=-sym) { int temp=C(i,p)*C(k-p*j,i)%mod; if(temp) add(sum,sym*temp+((~sym)?0:mod)); else break; } return sum; } #undef int int main() { #define int long long freopen("legend.in","r",stdin); freopen("legend.out","w",stdout); n=read(); m=read(); lim=read(); mx=max(n,max(m,lim)); fac[0]=ifac[0]=1; for(int i=1;i<=mx;i++) fac[i]=fac[i-1]*i%mod; ifac[mx]=power(fac[mx],mod-2); for(int i=mx-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod; for(int i=0;i<=n;i++) for(int j=1;j<=lim;j++) for(int k=1;k<=n-i;k++) { int base=0,temp=C(n,i)%mod*g(i,j-1,m-j*k)%mod; if(!temp) continue; for(int p=k;p<=n-i;p++) { int temp=C(n-i,p); if(!temp) break; add(base,temp*power(lim-j,n-i-p)%mod); } if(base) add(ans,base*temp%mod); } printf("%lld",ans*power(power(lim,n),mod-2)%mod); return 0; }

T4 数树

解题思路#

枚举以哪个节点为根以及子树和 T2 的匹配程度来判断。

具体实现可以 Hash+素数 防止重复。

code#

Copy
#include<bits/stdc++.h> #define int long long #define ull unsigned long long #define f() cout<<"RP++"<<endl using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f; } const int N=3e3+10,M=20,bas=23,mod=998244353; int n,m,cnt,ans,pri[N],siz[N]; int tot=1,head[N],ver[N<<1],nxt[N<<1]; ull has[N]; unordered_map<ull,int> ys,f[N]; unordered_map<ull,bool> can,mp; vector<int> v[M]; bitset<N> vis; void add_edge(int x,int y) { ver[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void Get_Prime() { for(int i=2;i<=1000;i++) { if(!vis[i]) pri[++cnt]=i; for(int j=1;j<=cnt&&pri[j]*i<=1000;j++) { vis[i*pri[j]]=true; if(i%pri[j]==0) break; } } } void pre_dfs(int x,int fa) { siz[x]=has[x]=1; for(auto to:v[x]) { if(to==fa) continue; pre_dfs(to,x); siz[x]+=siz[to]; has[x]+=has[to]*pri[siz[to]+bas]; } mp.insert(make_pair(has[x],true)); ys.insert(make_pair(has[x],siz[x]+bas)); } void dfs(int x,int fa) { f[x].insert(make_pair(1,1)); for(int i=head[x];i;i=nxt[i]) { int to=ver[i]; if(to==fa) continue; unordered_map<ull,int> temp=f[x]; dfs(to,x); for(auto it1:temp) for(auto it2:f[to]) { ull rec=it1.first+it2.first*pri[ys.find(it2.first)->second]; f[x][rec]=(f[x][rec]+it1.second*it2.second)%mod; } } vector<ull> dela; for(auto it:f[x]) if(mp.find(it.first)==mp.end()) dela.push_back(it.first); for(auto it:dela) f[x].erase(it); } #undef int int main() { #define int long long freopen("count.in","r",stdin); freopen("count.out","w",stdout); n=read(); Get_Prime(); for(int i=1,x,y;i<n;i++) x=read(),y=read(), add_edge(x,y),add_edge(y,x); m=read(); for(int i=1,x,y;i<m;i++) x=read(),y=read(), v[x].push_back(y),v[y].push_back(x); for(int i=1;i<=m;i++) pre_dfs(i,0),can.insert(make_pair(has[i],true)); dfs(1,0); for(auto it:can) for(int i=1;i<=n;i++) if(f[i].find(it.first)!=f[i].end()) ans=(ans+f[i].find(it.first)->second)%mod; printf("%lld",ans); return 0; }
posted @   Varuxn  阅读(148)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
目录