noip模拟6

A 选彩笔(rgb)

一眼转三维坐标系搞。

但是最开始想歪了,以为要转曼哈顿距离,但是发现三维切比雪夫距离不支持转曼哈顿距离。

补充一个知识点
x 维的曼哈顿距离支持转到 2x1 维的切比雪夫距离
所以一维和二维可以直接转化,但是三维及以上就不行了。三维曼哈顿距离等同于某种坐标系下的四维切比雪夫距离。

然后想到题目就是在求最最的一个立方体满足立方体中点的个数为 k 个。

然后就想到了三维前缀和,值域 [0255] 可做。

但是总体复杂度是 O(n2) 的,没多少分。

其实在三维前缀和的基础上改成二分答案,每次 check 搜索值域内全部边长为 mid 的立方体,用三维前缀和查个数是否大于等于 k 即可。

点击查看代码
//自己的没调出来崩溃了,这个是别人的。
#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)

非常好 gxyzOJ 我随便写的 dfs 都有 65

这个题正解已经想出来了,但是没敢打。

考虑每一对失配的点,如果合法,一定是到最终位置为逆序,那么每次 swap 保证其它点相对位置不变,对于一个点最多移动 O(n),类似于冒泡排序。

意思就是忽略题目给的排序条件,每次只进行 swap 来保证正确性,然后每个点 O(n),总复杂度 O(n2)

其实我是考虑到移动次数会大于 n2。但是不会,最大才会 n22 次左右(就是倒序转顺序的类型)

点击查看代码
#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)

对于 m 进制的数,求解的个数,就是解 L1 个方程。

给定 1<aL,p<m,sa(m1),求 i=1axi=s,0xi<mx1<p 的非负整数解的个数。

先不考虑 x1 的条件如何操作,我们考虑容斥,钦定 k 个元素一定大于等于 m,那剩下 a 个数的和为 skm,有 (skm+a1a1) 组解。

fa(s)=k=0a(1)k(ak)(skm+a1a1)

考虑 x1 后:

ans=i=0p1fa1(si)

来源于题解

点击查看代码
#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)

有一个 30 分的思路。

考虑树上只有一个银行,答案的最小值是什么。

考虑一条路径 (x,fa) 的贡献,是 w 乘以通过这条路径的总居民个数

那么,对于每一条边,我们都有两种选择:

  • 选择子树内的所有居民通过这条边(银行在子树外);

  • 选择子树外的所有居民通过这条边(银行在子树内)。

简单来说,就是

(x,fa)w×min(sizex,Ssizex)

其中 sizex 表示以 x 为根的子树居民数量S 表示这棵树全部的居民数量。

对于有两个银行的情况,我们知道,一定会有一条边没有居民通过,这条边左边的居民去一个银行,右边去另一个银行。

可以枚举所有的边,考虑将这条边断开,分裂成两棵树,分别用上诉做法求解答案,那这条边的最小答案就是两树答案之和。

具体地,对于枚举到一条边 (u,v),其中 uv 的父亲:

  • v 的子树,答案取 (x,fa)vw×min(sizex,sizevsizex)

  • 另一棵树:

  • 若当前点的子树有 v,答案在这里取 w×min(sizeusizev,Ssizeu)

  • 否则,答案在这里取 w×min(sizeu,Ssizeusizev)

就好了。

点击查看代码
#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;
}
posted @   ccjjxx  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示