模拟赛8 题解 三道好题

文章目录

game

一个n*m的矩阵,总共删掉k个数,每次选择一行删掉里面最大的数字,求删掉数字总和的最小值.
非常快地想到一个贪心:每次删掉最大数最小的行的最大数.
被轻松hack.

2 3 4
4 4 4
5 1 1

再往深了想:每次删掉某一行的最大数之后,必须删掉这一行的所有数,不然显然不值.
那么找到所有数之和最小的几行,如果和相同按照最大的几个值从小到大排.
最后把去掉很多行剩下的数字 r e m rem rem每行按照 r e m rem rem个数的和排序,排序方法和上面一样.
这样就可以…轻松获得 30 30 30分.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=1018;
int a[aoi][aoi],n,m,k,b[aoi][aoi];
struct nico{
int id; ll sum;
bool operator <(const nico &b) const{
  if (sum^b.sum) return sum<b.sum;
  for (int i=1;i<=n;++i)
    if (a[id][i]^a[b.id][i])
      return a[id][i]>a[b.id][i];
  return id<b.id;
  }
}node[aoi];
 
int main(){
int i,j;
n=read(),m=read(),k=read();
for (i=1;i<=n;sort(a[i]+1,a[i]+m+1,greater<int>()),++i)
  for (j=1;j<=m;++j) node[i].id=i,node[i].sum+=a[i][j]=read();
sort(node+1,node+n+1);
memcpy(b,a,sizeof a);
for (i=1;i<=n;++i)
  for (j=1;j<=m;++j) a[i][j]=b[node[i].id][j];
ll llx=0;
for (i=1;i<=n&&k>=m;++i) llx+=node[i].sum,k-=m;
int now=i;
for (i=now;i<=n;++i)
  for (node[i]=nico{i,0ll},j=1;j<=k;++j)
    node[i].sum+=a[i][j];
sort(node+now,node+n+1);
for (i=1;i<=k;++i) llx+=a[node[now].id][i];
write(llx);
}

为什么呢?
对于某一行来说,这行既有可能全部取,也有可能只取 r e m rem rem个.
那么我们枚举所有行,在这行取 r e m rem rem个,剩下的行我们用上面的方法贪心即可.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=1018;
ll a[aoi][aoi],tmp[aoi];
int main(){
int i,j,n,m,k,l;
n=read(),m=read(),k=read();
for (i=1;i<=n;++i){ 
  for (j=1;j<=m;++j) a[i][j]=read();
  sort(a[i]+1,a[i]+m+1,greater<ll>());
  for (j=1;j<=m;++j) a[i][j]+=a[i][j-1];
  }
int net=k/m,rem=k%m;
if (!rem) rem=m,net--; // 特判rem等于0,否则当k=n*m的时候会爆炸.
ll llx=1e18,zxy;
for (i=1;i<=n;++i){
  for (*tmp=zxy=0,j=1;j<=n;++j)
    if (j^i) tmp[++*tmp]=a[j][m];
  sort(tmp+1,tmp+*tmp+1);
  for (j=1;j<=net;++j) zxy+=tmp[j];
  llx=min(llx,zxy+a[i][rem]);
  }
write(llx);
}

tree

给 一 棵 树 , 求 符 合 以 下 条 件 的 四 元 组 ( a , b , c , d ) 的 总 数 . 1. d i s ( a , b ) = p 2. d i s ( c , d ) = q 3. ( a , b ) 和 ( c , d ) 两 条 路 径 没 有 交 点 . n ≤ 3000. 给一棵树,求符合以下条件的四元组(a,b,c,d)的总数.\newline 1.dis(a,b)=p \newline 2.dis(c,d)=q\newline 3.(a,b)和(c,d)两条路径没有交点.\newline n\leq 3000. ,(a,b,c,d).1.dis(a,b)=p2.dis(c,d)=q3.(a,b)(c,d).n3000.

枚举 a , b a,b a,b,满足条件的 c , d c,d c,d满足下列两种情况的一种.

  1. l c a ( c , d ) lca(c,d) lca(c,d) l c a ( a , b ) lca(a,b) lca(a,b)的子树内.
  2. l c a ( c , d ) lca(c,d) lca(c,d)不在 l c a ( a , b ) lca(a,b) lca(a,b)的子树内.

那么对于第一种情况, l c a ( c , d ) lca(c,d) lca(c,d)不在 a → b a\to b ab的路径上就可以了.
预处理 f [ i ] f[i] f[i]为经过 i i i长度为 q q q的路径数.
对于第二种情况, c → d c\to d cd的路径不经过连接 l c a ( a , b ) lca(a,b) lca(a,b) f a [ l c a ( a , b ) ] fa[lca(a,b)] fa[lca(a,b)]的边就可以了.
预处理经过每一条边,长度为 q q q的路径就可以了.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const int aoi=3018; 
vector<int> lj[aoi];
typedef int lxy[aoi];
lxy a,b,dep,fa,pa[aoi]; // a是经过i长度为q的路径数,b是经过i与fa[i]的边,长度为q的路径数.
void dfs(int u,int f){
dep[u]=dep[fa[u]=f]+1;
for (int v:lj[u]) if (v^f) dfs(v,u);
}
int lca(int u,int v){
return u==v?u:
     ~pa[u][v]?pa[u][v]:
     pa[u][v]=dep[u]>dep[v]?lca(fa[u],v):lca(u,fa[v]);
}//记忆化预处理lca
int dist(int u,int v){
return dep[u]+dep[v]-(dep[lca(u,v)]<<1);
}//两点间距离
void dfs(int u){
a[u]+=a[fa[u]];
for (int v:lj[u]) if (v^fa[u]) dfs(v),b[u]+=b[v];
}
int main(){
int n,p,q,i,j; n=read(),p=read(),q=read();
for (i=1;i<n;++i){
  int u=read(),v=read();
  lj[u].push_back(v);
  lj[v].push_back(u);
  }dfs(1,0);
memset(pa,-1,sizeof pa);
int cnt=0; ll llx=0;
for (i=1;i<=n;++i)
  for (j=1;j<=n;++j) if (dist(i,j)==q)
    ++cnt,++a[lca(i,j)],++b[i],++b[j],b[lca(i,j)]-=2;
dfs(1);
for (i=1;i<=n;++i)
  for (j=1;j<=n;++j) if (dist(i,j)==p)
    llx+=cnt-a[i]-a[j]+a[lca(i,j)]+a[fa[lca(i,j)]]-b[lca(i,j)];
write(llx);
}

perm

构 造 一 个 排 列 使 得 ∑ i = 1 n i   m o d   p i = s 构造一个排列使得\sum_{i=1}^{n}i\ mod\ p_{i}=s 使i=1ni mod pi=s.
首先我们发现 s s s的最大值是 n × ( n − 1 ) 2 . \frac{n\times(n-1)}{2}. 2n×(n1).
接下来我们通过构造证明 0 → n × ( n − 1 ) 2 0\to \frac{n\times(n-1)}{2} 02n×(n1)的值都可以取到.
首先令 p [ n ] = 1 , p [ 1 ] = 2 , p [ i ] = i ( i ∈ { 2 , 3 , 4 , . . . , n − 1 } ) , i ∉ g p[n]=1,p[1]=2,p[i]=i (i\in \{2,3,4,...,n-1\}),i\notin g p[n]=1,p[1]=2,p[i]=i(i{2,3,4,...,n1}),i/g,其中 g ⊆ { 2 , 3 , 4 , . . . , n − 1 } g\subseteq \{2,3,4,...,n-1\} g{2,3,4,...,n1},然后从小到大枚举 i ∈ g i\in g ig,将 p [ i ] p[i] p[i]赋值为最小没有选择过的数.这样对于 i ∈ g i\in g ig来说, p [ i ] > i p[i]>i p[i]>i,故此法可行.
那么只剩下构造 s ∈ { 0 , 2 , n × ( n − 1 ) 2 − 1 } s\in\{0,2,\frac{n\times(n-1)}{2}-1\} s{0,2,2n×(n1)1}的情况.
1. 当 s = 0 时 , p [ i ] = i . 2. 当 s = 2 时 , p [ 1 ] = 3 , p [ 2 ] = 1 , p [ 3 ] = 2 , 剩 下 p [ i ] = i . 3. 当 s = n × ( n − 1 ) 2 − 1 时 : 若 n 是 奇 数 , p [ 1 ] = 3 , p [ 2 ] = 1 , p [ n ] = 2 , 剩 下 p [ i ] = i + 1. 若 n 是 偶 数 , p [ 1 ] = 1 , p [ n ] = 2 , 剩 下 p [ i ] = i + 1. 1.当s=0时,p[i]=i.\newline 2.当s=2时,p[1]=3,p[2]=1,p[3]=2,剩下p[i]=i.\newline 3.当s=\frac{n\times(n-1)}{2}-1时: \newline 若n是奇数,p[1]=3,p[2]=1,p[n]=2,剩下p[i]=i+1. \newline 若n是偶数,p[1]=1,p[n]=2,剩下p[i]=i+1. 1.s=0,p[i]=i.2.s=2,p[1]=3,p[2]=1,p[3]=2,p[i]=i.3.s=2n×(n1)1:n,p[1]=3,p[2]=1,p[n]=2,p[i]=i+1.n,p[1]=1,p[n]=2,p[i]=i+1.
好了构造完成.

#include<bits/stdc++.h> //Ithea Myse Valgulious
using namespace std;
const char no_solution[]="nO 5oLuTi0N";
const int yuzu=1e5;
typedef int fuko[yuzu|10];
int main(){
ll n,s; read(n),read(s);
if (s*2>n*(n-1)) return puts(no_solution),0;
if (!s){
  for (int i=1;i<=n;++i) write(i),p32;
  return 0;
  }
if (s==2){
  printf("3 1 2 ");
  for (int i=4;i<=n;++i) write(i),p32;
  return 0;
  }
if (s*2+2==n*(n-1)){
  if (n&1){
    printf("3 1 ");
    for (int i=3;i<n;++i) write(i+1),p32;
    puts("2");
    }
  else{
    printf("1 ");
    for (int i=2;i<n;++i) write(i+1),p32;
    puts("2");
    }
  return 0;
  }
static fuko a,b;
a[n]=b[n]=1;
for (int i=n-1;i>1;--i)
  if (s-i>0&&(s-i)^2) b[i]=1,s-=i;
for (int i=2;i<n;++i) if (!b[i]) a[i]=i;
int p=2;
for (int i=1;i<=n;++i)
  if (!a[i]){
    for (;!b[p];++p);
    a[i]=p++;
  }
for (int i=1;i<=n;++i) printf("%d ",a[i]);
}

谢谢大家.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值