noip模拟2
A 四舍五入
据说是签,但是我没签出来。
观察到,题目要求的东西就是:
我们这样思考:
令 ,那么有 。
进一步地,
我们假设 是个整数,那么
根据不等关系,我们猜测:令 ,有 。
意思是,如果 ,那么也满足上述题目要求的关系。
这样问题就被转化了。
对于每一个 的倍数 ,区间 的值都会有一个 的贡献。
每次用差分统计即可。时间复杂度 。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long int n; const int N=2e6+6; void write(int x) { if(x>9) write(x/10); putchar(x%10+'0'); } int a[N]; signed main() { freopen("count.in","r",stdin); freopen("count.out","w",stdout); scanf("%lld",&n); for(int j=1;j<=n;j++) { for(int i=0;i<=n;i+=j) { int l=i,r=i+(j-1)/2+1; a[l]++,a[r]--; } } for(int i=1;i<=n;i++)a[i]+=a[i-1],write(a[i]),putchar(' '); return 0; }
B 填算符
这个题其实离正解很近了。
先说 是 的幂次的解法。
因为 ,所以我们直接进行一个奇妙的哈希,哈希函数是 。
然后就得到了一堆小于 的数,方便我们操作。
观察样例给出的答案,根据答案反推过程在这里极为好用。
我们定位每一个答案选取的点位,发现前半部分全部是顺序递增的,后面的跨度很大。
那这就说明答案会有两部分组成:一部分是特殊的,另一部分是连续的、剩余的。
再次找规律,看到答案最后面的那些对应的点位前一个数都是 。
为什么是 ?
为什么不是别的数?
我们观察到,最后一个答案后面的所有数都没有 ,但是其他的数都有。
我们记录所有数最后一次出现的位置,发现 是最靠前的!
那这个策略就很明晰了,选取最后一次出现最早的数,让它和后面与起来,在最后保证所有能取到的二进制位都能取到。
那为什么最后一个之前还有那么多要取到 呢?
这是因为,你需要在最后与答案的前面留下一个 号位置,要不然取不到了。
那为什么前面要连续地取呢?
这是因为可能把 取完会比 少,那就需要在超出范围前把剩下没取到的位置加到答案中。
但是在答案里, 的位置需要一直保留,因为有最优性决策问题,你每次从后往前选取 的位置一定是越靠后越优秀,那只有在 的位置继承上一个 ,抛弃其他位置才是最优。
就是这样。
点击查看代码
for(int i=1;i<=n;i++) a[i]=__lg(a[i]),pre[i]=t[a[i]],t[a[i]]=i,++cnt[a[i]]; int mi=1e9,pos=0; for(int i=0;i<=60;i++) if(t[i]) if(mi>t[i]&&t[i]>k+1) mi=t[i],pos=i; int p=0; for(int i=n;i>=1;i--) { if(a[i]==pos) { if(e.size()+pre[i]<=k){p=i;break;} e.push_back(i-1); } } for(int i=p-1;e.size()<k;i--) e.push_back(i-1); sort(e.begin(),e.end()); for(int v:e) cout<<v<<" ";
再看正解。
首先,最终的最大答案一定是取前 个空放 后面的全放 。
然后从高往低,对于每一位,和上面思路一样去判断最后一个位置后的答案是否是最优。
只要找到一个,那么就可能从后往前找有这一位的数,直接加入答案,向前更新即可。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long int n,k; const int N=1e6+6; int a[N],s[N],l[N]; vector<int>e; int ans; signed main() { freopen("bitop.in","r",stdin); freopen("bitop.out","w",stdout); cin>>n>>k;int mx=0,mw=0; for(int i=1;i<=n;i++) cin>>a[i],mx=max(a[i],mx); for(int i=59;i>=0;--i) if(mx&(1<<i))mw=1<<i; ans=a[1]; for(int i=1;i<=k+1;++i) ans&=a[i]; for(int i=k+2;i<=n;++i) ans|=a[i-1]; for(int i=n;i;--i)s[i]=s[i+1]|a[i]; int len=n; int mn=1ll<<59; for(int i=59;i>=0;--i)//要取到这个位置的答案,就要与起来这个位置最后一个数 { int x=1ll<<i,la=0; if(ans&x) { for(int j=1;j<=len;++j) if(a[j]&x)l[j]=la,la=j;//l:上一个有这个位置的数的id len=la;mn=min(mn,x); if(s[len+1]==ans)break;// 从 lst+1到n的位置满足答案,那这前面的都取。 else { for(int j=1;j<=n;++j) a[j]-=s[len+1]&a[j]; ans-=s[len+1]; for(int j=n;j;--j)s[j]=s[j+1]|a[j]; } } } int now=len; for(int i=l[len],tmp=0;i;i=l[i])//按照最大的,最后的数开始取,和幂次点策略一致 { ++tmp; if(tmp==k) { for(int i=len;i!=now;i=l[i])e.push_back(i-1),--k; e.push_back(--now); sort(e.begin(),e.end()); for(int v:e) cout<<v<<" "; exit(0); } if(i-2>=k-tmp)now=i; else break; } for(int i=len;i!=now;i=l[i])e.push_back(i-1),--k; --now; while(k--)e.push_back(--now); sort(e.begin(),e.end()); for(int v:e) cout<<v<<" "; }
C 道路修建
太魔怔了这道题。
无法改。
路径长度和可转化为边经过的次数。
对于一条边,分成两类:环上的边和子树内的边。
对于环上的边,经过次数是 ;
对于子树内的边,它经过的次数是原本经过次数+加边后的新增次数。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long const int N=3e5+5; const int mod=998244353; vector<int>e[N]; int n,q,tp,dep[N],siz[N],son[N],id[N],top[N],rk[N],tim,f[N],k[N],fa[N],ANS; void dfs1(int u,int F) { siz[u]=1,dep[u]=dep[F]+1,fa[u]=F; for(int v:e[u]) { if(v==F) continue; dfs1(v,u);siz[u]+=siz[v]; f[u]=(f[u]+f[v]+siz[v])%mod; if(siz[v]>siz[son[u]]) son[u]=v; } k[u]=siz[u]*(n-siz[u])%mod; ANS=(ANS+k[u])%mod; } int p[N],g[N],tot[N]; void dfs2(int u,int t) { top[u]=t,id[u]=++tim,rk[tim]=u; if(son[u]) { p[u]=(f[u]-f[son[u]]-siz[son[u]]+mod*2)%mod; g[u]=siz[u]-siz[son[u]]; tot[son[u]]=(tot[u]+n-2*siz[son[u]]+mod*2)%mod; dfs2(son[u],t); } for(int v:e[u]) { if(v==fa[u]||v==son[u]) continue; tot[v]=(tot[u]+n-2*siz[v]+mod*2)%mod; dfs2(v,v); } } int calc(int v) { return v*(v-1)/2%mod; } struct SegTree{ struct node{ int s1,s2,s3; }tr[N<<2]; #define lid now<<1 #define rid now<<1|1 void pushup(int now) { tr[now].s1=tr[lid].s1+tr[rid].s1,tr[now].s2=tr[lid].s2+tr[rid].s2,tr[now].s3=tr[lid].s3+tr[rid].s3; } void build(int now,int l,int r) { if(l==r){int d=rk[l];tr[now].s1=k[d],tr[now].s2=p[d]*(n-g[d])%mod,tr[now].s3=calc(g[d]);return ;} int mid=(l+r)>>1; build(lid,l,mid),build(rid,mid+1,r),pushup(now); } int query(int now,int l,int r,int x,int y,int op) { if(x<=l&&r<=y) { if(op==0) return tr[now].s1; if(op==1) return tr[now].s2; if(op==2) return tr[now].s3; } int mid=(l+r)>>1,res=0; if(x<=mid) res=(res+query(lid,l,mid,x,y,op)); if(y>mid) res=(res+query(rid,mid+1,r,x,y,op)); return res; } }st; int ask(int x,int y) { if(x==y)return 0; int X=x,Y=y,fx=top[x],fy=top[y],lstx=0,lsty=0,res[3]={0,0,0}; while(fx!=fy) { if(dep[fx]<dep[fy])swap(X,Y),swap(x,y),swap(fx,fy),swap(lstx,lsty); res[0]=(res[0]+st.query(1,1,n,id[fx],id[x],0))%mod; if(fx!=x)res[1]=(res[1]+st.query(1,1,n,id[fx],id[x]-1,1))%mod; if(x==X)res[1]=(res[1]+f[x]*(n-siz[x]))%mod; else res[1]=(res[1]+(f[x]-f[lstx]-siz[lstx]+mod*2)*(n-(siz[x]-siz[lstx]))%mod)%mod; if(fx!=x)res[2]=(res[2]+st.query(1,1,n,id[fx],id[x]-1,2))%mod; if(x==X)res[2]=(res[2]+calc(siz[x]))%mod; else res[2]=(res[2]+calc(siz[x]-siz[lstx]))%mod; lstx=fx,x=fa[fx],fx=top[x]; } if(dep[x]<dep[y])swap(X,Y),swap(x,y),swap(lstx,lsty); int lca=y; if(x!=y) { res[0]=(res[0]+st.query(1,1,n,id[y]+1,id[x],0))%mod; if(id[x]-id[y]+1>=3) res[1]=(res[1]+st.query(1,1,n,id[y]+1,id[x]-1,1))%mod,res[2]=(res[2]+st.query(1,1,n,id[y]+1,id[x]-1,2))%mod; if(id[x]-id[y]+1>=2) { if(x!=X)res[1]=(res[1]+(f[x]-f[lstx]-siz[lstx]+mod*2)*(n-(siz[x]-siz[lstx]))%mod)%mod,res[2]=(res[2]+calc(siz[x]-siz[lstx]))%mod; else res[1]=(res[1]+f[x]*(n-siz[x]))%mod,res[2]=(res[2]+calc(siz[x]))%mod; } res[1]=(res[1]+(tot[lca]-f[son[lca]]-f[lsty]-siz[son[lca]]-siz[lsty])*(siz[son[lca]]+siz[lsty]))%mod,res[2]=(res[2]+calc(n-siz[son[lca]]-siz[lsty]))%mod; } else { res[1]=(res[1]+(tot[lca]-f[lstx]-f[lsty]-siz[lstx]-siz[lsty])*(siz[lstx]+siz[lsty]))%mod,res[2]=(res[2]+calc(n-siz[lstx]-siz[lsty]))%mod; } int dis=dep[X]+dep[Y]-2*dep[lca]+1; res[2]=(calc(n)-res[2]+mod)%mod; int ans=((res[1]+dis*res[2]-res[0])%mod+mod)%mod; return ans; } signed main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>q>>tp; for(int i=1;i<n;i++) { int u,v;cin>>u>>v; e[u].push_back(v),e[v].push_back(u); } dfs1(1,0),tot[1]=f[1],dfs2(1,1); st.build(1,1,n); int lstans=0; while(q--) { int u,v;cin>>u>>v; if(tp) u^=lstans,v^=lstans; cout<<(lstans=(ask(u,v)+ANS)%mod)<<"\n"; } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!