noip模拟6
A 选彩笔(rgb)
一眼转三维坐标系搞。
但是最开始想歪了,以为要转曼哈顿距离,但是发现三维切比雪夫距离不支持转曼哈顿距离。
补充一个知识点
维的曼哈顿距离支持转到 维的切比雪夫距离
所以一维和二维可以直接转化,但是三维及以上就不行了。三维曼哈顿距离等同于某种坐标系下的四维切比雪夫距离。
然后想到题目就是在求最最的一个立方体满足立方体中点的个数为 个。
然后就想到了三维前缀和,值域 可做。
但是总体复杂度是 的,没多少分。
其实在三维前缀和的基础上改成二分答案,每次 check
搜索值域内全部边长为 的立方体,用三维前缀和查个数是否大于等于 即可。
点击查看代码
//自己的没调出来崩溃了,这个是别人的。 #include <bits/stdc++.h> #define ll long long using namespace std; const int N = 1e5+5,INF=1E9; inline ll read() { ll x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48); return x*f; } inline void write(ll x) { if(x<0)x=-x,putchar('-'); if(x>9)write(x/10); putchar((x%10)|48); } inline void write(ll x,char p){ write(x);putchar(p); } int n,K; struct ac { int r,g,b; }a[N]; int c[260][260][260]; inline int get(int a,int b,int C,int x,int y,int z){ return c[x][y][z]-c[a-1][y][z]-c[x][b-1][z]-c[x][y][C-1]+c[a-1][b-1][z]+c[a-1][y][C-1]+c[x][b-1][C-1]-c[a-1][b-1][C-1]; } inline bool check(int mid){ for(int i=1;i+mid<=256;i++){ for(int j=1;j+mid<=256;j++){ for(int k=1;k+mid<=256;k++){ if(get(i,j,k,i+mid,j+mid,k+mid)>=K){return 1;} } } } return 0; } inline void fen(int l,int r){ int ans=0; while(l<=r){ int mid=l+r>>1; if(check(mid)){ ans=mid;r=mid-1; }else l=mid+1; } write(ans); } int main() { freopen("rgb.in","r",stdin),freopen("rgb.out","w",stdout); n=read();K=read(); for(int i=1;i<=n;i++){ a[i]={read()+1,read()+1,read()+1}; // v[a[i].r][a[i].g][a[i].b].pb(i); c[a[i].r][a[i].g][a[i].b]++; } for(int i=1;i<=256;i++){ for(int j=1;j<=256;j++){ for(int k=1;k<=256;k++){ c[i][j][k]+=c[i-1][j][k]; } } } for(int i=1;i<=256;i++){ for(int j=1;j<=256;j++){ for(int k=1;k<=256;k++){ c[i][j][k]+=c[i][j-1][k]; } } } for(int i=1;i<=256;i++){ for(int j=1;j<=256;j++){ for(int k=1;k<=256;k++){ c[i][j][k]+=c[i][j][k-1]; } } } fen(0,255); return 0; }
B 兵蚁排序(sort)
非常好 我随便写的 都有 。
这个题正解已经想出来了,但是没敢打。
考虑每一对失配的点,如果合法,一定是到最终位置为逆序,那么每次 swap
保证其它点相对位置不变,对于一个点最多移动 ,类似于冒泡排序。
意思就是忽略题目给的排序条件,每次只进行 swap
来保证正确性,然后每个点 ,总复杂度 。
其实我是考虑到移动次数会大于 。但是不会,最大才会 次左右(就是倒序转顺序的类型)
点击查看代码
#include<bits/stdc++.h> using namespace std; const int N=1e3+5; int read() { int num=0,typ=1,c=getchar(); while('0'>c||c>'9') { if(c=='-') typ=-1; c=getchar(); }while('0'<=c&&c<='9') { num=num*10+c-48; c=getchar(); }return num*typ; } int t,n; int a[N],b[N]; int tot,l[N*N],r[N*N]; int main() { freopen("sort.in","r",stdin); freopen("sort.out","w",stdout); t=read(); while(t--) { n=read();tot=0; for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=n;++i)b[i]=read(); for(int i=1;i<=n;++i) { if(a[i]==b[i]) continue; int j=n+1; for(j=i+1;j<=n;++j) if(a[j]==b[i]) break; if(j>n) { tot=-1;break; } for(int k=j;k>i;k--) { if(a[k]<a[k-1]) { tot++,l[tot]=k-1,r[tot]=k,swap(a[k-1],a[k]); } else { tot=-1;break; } } if(tot==-1) break; } if(tot==-1) { printf("-1\n"); continue; }else { printf("0\n%d\n",tot); for(int i=1;i<=tot;++i) printf("%d %d\n",l[i],r[i]); } } return 0; }
C 人口局 DBA(dba)
对于 进制的数,求解的个数,就是解 个方程。
给定 ,求 的非负整数解的个数。
先不考虑 的条件如何操作,我们考虑容斥,钦定 个元素一定大于等于 ,那剩下 个数的和为 ,有 组解。
考虑 后:
来源于题解
点击查看代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=2e3+10,M=4e6,mod=1e9+7; int m,n,sum,ans,a[N],fac[M+10],inv[M+10]; int qpow(int a,int b) { int res=1; while(b) { if(b&1) res=(res*a)%mod; a=(a*a)%mod,b>>=1; }return res; } void init() { fac[0]=inv[0]=1; for(int i=1;i<=M;i++) fac[i]=fac[i-1]*i%mod; inv[M]=qpow(fac[M],mod-2); for(int i=M-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod; } int getc(int n,int m) { if(m<0||n<m)return 0; return fac[n]*inv[m]%mod*inv[n-m]%mod; } signed main() { freopen("dba.in","r",stdin); freopen("dba.out","w",stdout); cin>>m>>n; for(int i=n;i;i--) cin>>a[i],sum+=a[i]; init(); for(int i=n,cnt=0,flag=1;i;i--,cnt=0,flag=1) { for(int j=0;j<=sum/m;flag=-flag,j++) { cnt+=(flag*getc(i-1,j)+mod)*(getc(sum-j*m+i-1,i-1)-getc(sum-a[i]+1-j*m+i-2,i-1)+mod)%mod; } sum-=a[i],ans=(ans+cnt%mod)%mod; } cout<<ans; return 0; }
D 银行的源起(banking)
有一个 分的思路。
考虑树上只有一个银行,答案的最小值是什么。
考虑一条路径 的贡献,是 乘以通过这条路径的总居民个数。
那么,对于每一条边,我们都有两种选择:
-
选择子树内的所有居民通过这条边(银行在子树外);
-
选择子树外的所有居民通过这条边(银行在子树内)。
简单来说,就是
其中 表示以 为根的子树居民数量, 表示这棵树全部的居民数量。
对于有两个银行的情况,我们知道,一定会有一条边没有居民通过,这条边左边的居民去一个银行,右边去另一个银行。
可以枚举所有的边,考虑将这条边断开,分裂成两棵树,分别用上诉做法求解答案,那这条边的最小答案就是两树答案之和。
具体地,对于枚举到一条边 ,其中 是 的父亲:
-
的子树,答案取
-
另一棵树:
-
若当前点的子树有 ,答案在这里取
-
否则,答案在这里取
就好了。
点击查看代码
#include<bits/stdc++.h> using namespace std; #define int long long int T; int n;const int N=1e5+4; vector<int>e[N];int ssiz[N]; int a[N]; int dep[N],siz[N],id[N],rk[N],tim,fa[N],top[N],son[N]; void dfs1(int u,int f) { fa[u]=f,dep[u]=dep[f]+1,siz[u]=1;ssiz[u]=a[u]; for(int i=0;i<e[u].size();i++) { int v=e[u][i]; if(v==f) continue; dfs1(v,u); siz[u]+=siz[v];ssiz[u]+=ssiz[v]; if(siz[v]>siz[son[u]]) son[u]=v; } } void dfs2(int u,int t) { top[u]=t;id[u]=++tim; if(son[u]) dfs2(son[u],t); for(int i=0;i<e[u].size();i++) { int v=e[u][i]; if(v==son[u]||v==fa[u]) continue; dfs2(v,v); } } int d[N]; vector<int>w[N]; inline int read() { int w{1},x{}; char c=getchar(); while(c<'0'||c>'9'){if(c == '-')w=-1;c=getchar();} while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+c-'0',c=getchar(); return w * x; } inline void write(int x) { if(x<0)x=-x,putchar('-'); if(x>9)write(x/10); putchar(x%10+'0'); } inline void writeln(int x){write(x);putchar(10);} inline void writek(int x){write(x);putchar(' ');} int disu,disv; int sum1=0,sum2=0; void calc(int u) { for(int i=0;i<e[u].size();i++) { if(e[u][i]==fa[u])continue; sum1+=w[u][i]*min(ssiz[e[u][i]],ssiz[disv]-ssiz[e[u][i]]); calc(e[u][i]); } } void calc2(int u,int d) { for(int i=0;i<e[u].size();i++) { if(e[u][i]==fa[u]||e[u][i]==d) continue; if(id[d]>=id[e[u][i]]&&id[d]<id[e[u][i]]+siz[e[u][i]]) sum2+=w[u][i]*min(ssiz[e[u][i]]-ssiz[d],ssiz[1]-ssiz[e[u][i]]); else sum2+=w[u][i]*min(ssiz[e[u][i]],ssiz[1]-ssiz[e[u][i]]-ssiz[disv]); // cout<<ssiz[1]-ssiz[e[u][i]]-ssiz[disv]<<" "; calc2(e[u][i],d); } } signed main() { // freopen("sub2.in","r",stdin); freopen("banking.in","r",stdin); freopen("banking.out","w",stdout); T=read(); while(T--) { n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<n;i++) { int v=read(),u=read(),W=read(); e[u].push_back(v),e[v].push_back(u); w[u].push_back(W),w[v].push_back(W); } tim=0; dfs1(1,0),dfs2(1,1); int ans=1e18; for(int u=1;u<=n;u++) { for(int v:e[u]) { if(v==fa[u]) continue; disu=u,disv=v; sum1=sum2=0; calc(v),calc2(1,v); // cout<<sum1+sum2<<"\n"; ans=min(ans,sum1+sum2); } } writeln(ans); for(int i=1;i<=n;i++)e[i].clear(),w[i].clear(),top[i]=siz[i]=dep[i]=son[i]=id[i]=fa[i]=0; } 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框架的用法!