模拟赛8 题解 三道好题

文章目录

game

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

2 3 4
4 4 4
5 1 1

再往深了想:每次删掉某一行的最大数之后,必须删掉这一行的所有数,不然显然不值.
那么找到所有数之和最小的几行,如果和相同按照最大的几个值从小到大排.
最后把去掉很多行剩下的数字 r e m rem 每行按照 r e m rem 个数的和排序,排序方法和上面一样.
这样就可以…轻松获得 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 个.
那么我们枚举所有行,在这行取 r e m 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 a,b ,满足条件的 c , d c,d 满足下列两种情况的一种.

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

那么对于第一种情况, l c a ( c , d ) lca(c,d) 不在 a b a\to b 的路径上就可以了.
预处理 f [ i ] f[i] 为经过 i i 长度为 q q 的路径数.
对于第二种情况, c d c\to d 的路径不经过连接 l c a ( a , b ) lca(a,b) f a [ l c a ( a , b ) ] fa[lca(a,b)] 的边就可以了.
预处理经过每一条边,长度为 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 .
首先我们发现 s s 的最大值是 n × ( n 1 ) 2 . \frac{n\times(n-1)}{2}.
接下来我们通过构造证明 0 n × ( n 1 ) 2 0\to \frac{n\times(n-1)}{2} 的值都可以取到.
首先令 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 ,其中 g { 2 , 3 , 4 , . . . , n 1 } g\subseteq \{2,3,4,...,n-1\} ,然后从小到大枚举 i g i\in g ,将 p [ i ] p[i] 赋值为最小没有选择过的数.这样对于 i g i\in g 来说, p [ i ] &gt; i p[i]&gt;i ,故此此法可行.
那么只剩下构造 s { 0 , 2 , n × ( n 1 ) 2 1 } s\in\{0,2,\frac{n\times(n-1)}{2}-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.
好了构造完成.

#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]);
}

谢谢大家.

猜你喜欢

转载自blog.csdn.net/qq_31908675/article/details/82946210