2022牛客暑期多校第四场

【思维】N

【题意】

给 n 个整数,每次随机选择其中两个整数 a,b,将其替换为 a AND b 和 a OR b(位运算),直到稳定,求最终稳定状态的方差,输出最简分数形式。$2 \leq n \leq 10^5, 1 \leq a_i \leq 2^{15}$

【题解】

通过位运算表不难发现,替换之后相当于把 a 和 b 的二进制各个位上的 1 都尽量集中到一个数上,那么最终的稳定状态就是把所有数的二进制的各个位上的 1 都尽量集中起来。比如:

0 0 1 0 0 1          0 0 0 0 0 0

1 0 1 1 1 0   ->   0 0 1 1 0 1

0 1 0 1 0 1          1 1 1 1 1 1

算方差时用$Var(X) = E(X^2) - E(X)^2 = \frac{n\sum X^2 - (\sum X)^2}{n^2}$得到整数分子分母。由于每一位上 1 的个数不变,数的个数也不变,$\sum x$不变。

【代码】

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,cnt[20];
long long t,sum,mx,res;
void solve(long long x){   // 统计每一位上共有几个1
  int len=1;
  while (x){
    cnt[len]+=x%2;
    len++;
    x/=2;
  }
}
long long Pow(long long x,int y){
  long long res=1ll;
  while (y){
    if (y&1) res*=x;
    x*=x;
    y>>=1;
  }
  return res;
}
long long gcd(long long x,long long y){
  return y==0?x:gcd(y,x%y);
}
int main()
{
  scanf("%d",&n);
  for (int i=1;i<=n;i++){
    scanf("%lld",&t);
    solve(t);
    sum+=t;
  }
  for (int i=1;i<=n;i++){
    t=0ll;
    for (int j=15;j>=1;j--){   //最终稳定状态下的数
      if (cnt[j]) t+=Pow(2ll,(long long)j-1ll),cnt[j]--;
    }
    res+=t*t;   //sum(X^2)
  }
  sum*=sum;   //sum(X)^2
  res*=(long long)n;   //n*sum(X^2)
  long long g=gcd(res-sum,(long long)n*(long long)n);
  printf("%lld/%lld",(res-sum)/g,(long long)n*(long long)n/g);
  return 0;
}

【思维】K

【题意】

按顺序击杀 1 到 n 只怪,击杀第 i 只怪要求$A \equiv i\ (mod\ n)$,其中 A 的初值为 0,任意时刻可以对 A 进行任意次数的如下操作:选择一个整数$x \in [0,9]$,$A = 10A + x$。求最少操作几次。$1 \leq n \leq 10^6$

【题解】

假设当前值为 A,进行一次操作后$A \in [10A,10A+9]$。如果区间长度大于等于 n,则必然存在一个 x,使得同余式成立。如果区间长度小于 n,只需判断 i%n 是否被包含在内。对于新的 i,从小到大枚举操作次数即可。发现跑得很快,因此时间复杂度正确。

【代码】

#include <bits/stdc++.h>
using namespace std;
long long n,ans,l,r;
int main()
{
  scanf("%lld",&n);
  for (long long i=1;i<=n;i++){
    l=i-1; r=i-1;
    while (1){
      ans++;
      l=l*10; r=r*10+9;   //进行一次操作后的区间
      if (r-l+1>=n) break;
      if (l%n<=r%n&&l%n<=i%n&&r%n>=i%n) break;   // 0 --- l%n ———— r%n --- n-1 的情况
      if (l%n>r%n&&(l%n<=i%n||r%n>=i%n)) break;   // 0 ———— r%n --- l%n ———— n-1 的情况
    }
  }
  if (n!=1) printf("%lld",ans);
  else printf("0");
  return 0;
}

【构造+贪心】H

【题意】

给定一个整数 n,有 n 块 1x1,n-1 块 1x2,...,2 块 1x(n-1),1 块 1xn 的木板,求如何把他们拼成一个长方形,使得周长最小。输出每块木板左下角和右上角的坐标。$1 \leq T \leq 100, 1 \leq n \leq 100$

【题解】

解决边长:面积一定,越接近正方形周长约小,因此将最接近$\sqrt{S}$的一对因数作为边长,其中 S 为木板总面积。

解决怎么拼:贪心,每次选当前层放得下的最长的木板。经过验证发现可行。

【实现】

解决边长:将大的因数作为每层的长度,小的作为层的数量,因为小的有可能小于 n,即最长的木板的长度。

解决怎么拼:可以用 lower_bound 查找,添加 greater<int>()在单减序列中查找第一个小于等于目标的数,注意范围上界是 min(当前层剩余长度,n)

【代码】

#include <bits/stdc++.h>
using namespace std;
int T,n,cnt[105],flag[105],sum,a,ans[105][105][4],cur;
int find(int x){   //返回边长
  int b=(int)sqrt((double)x)+1;
  for (int i=b;i>=1;i--){
    if (x%i==0) return max(x/i,i);   //取较大的作为层宽
  }
}
void solve(){
  sum=0;   //总面积
  for (int i=1;i<=n;i++){
    cnt[i]=n-i+1;   //cnt[i]表示长度为i的木板还剩几个
    flag[i]=1;   //flag[i]表示长度为i的木板是否有剩余
    sum+=cnt[i]*i;
  }
  a=find(sum);
  printf("%d\n",2*(a+sum/a));
  for (int i=1;i<=sum/a;i++){
    cur=0;
    while (cur<a){
      int len=lower_bound(flag+1,flag+min(a-cur,n)+1,0,greater<int>())-flag-1;   //查找当前层放得下的最长的木板
      cnt[len]--;
      if (cnt[len]==0) flag[len]=0;
      int id=(n-len+1)-cnt[len];   //木板编号
      ans[len][id][0]=cur; ans[len][id][1]=i-1;
      ans[len][id][2]=cur+len; ans[len][id][3]=i;
      cur+=len;
    }
  }
}
int main()
{
  scanf("%d",&T);
  while (T--){
    scanf("%d",&n);
    solve();
    for (int i=1;i<=n;i++)
      for (int j=1;j<=(n-i+1);j++)
        printf("%d %d %d %d\n",ans[i][j][0],ans[i][j][1],ans[i][j][2],ans[i][j][3]);
  }
  return 0;
}

【三维前缀或】D

【题意】

有 n 个公司,每个公司有 mi 份工作,每份工作对 IQ,EQ,AQ 有最低门槛,满足同一家公司的任意一份工作的要求就可以去这家公司。q 次询问,每次给一个人的三商,问他能去几家公司,强制在线。$1 \leq n \leq 10, 1 \leq m_i \leq 10^5, 1 \leq IQ,EQ,AQ \leq 400, 1 \leq q \leq 2 \times 10^6$,时限3s。

【题解】

超大询问次数+强制在线+范围较小的值(400),考虑预处理。

如果第 i 家公司存在三商最低门槛为 x,y,z 的工作,则 a[x][y][z] 的二进制第 i 位为 1。对 a 维护三维前缀或(或运算不用容斥),对于询问 x,y,z,答案即为 sum[x][y][z] 的二进制表示中有多少 1。

【代码】

#include <bits/stdc++.h>
#include <random>
using namespace std;
const long long mod=998244353;
int lastans=0,n,q,m,seed,sum[405][405][405],a,b,c;
long long ans;
long long Pow(long long x,long long y){
  long long res=1ll;
  x%=mod;
  while (y){
    if (y&1) res=res*x%mod;
    x=x*x%mod;
    y>>=1;
  }
  return res;
}
int solve(int x,int y,int z){
  int res=0,tmp=sum[x][y][z];
  while (tmp){
    if (tmp&1) res++;
    tmp>>=1;
  }
  return res;
}
int main()
{
  scanf("%d%d",&n,&q);
  for (int i=1;i<=n;i++){
    scanf("%d",&m);
    for (int j=1;j<=m;j++){
      scanf("%d%d%d",&a,&b,&c);
      sum[a][b][c]|=(1<<(i-1));
    }
  }
  for (int i=1;i<=400;i++)
    for (int j=1;j<=400;j++)
      for (int k=1;k<=400;k++)
        sum[i][j][k]=sum[i][j][k]|sum[i-1][j][k]|sum[i][j-1][k]|sum[i][j][k-1];
  scanf("%d",&seed);
  std::mt19937 rng(seed);   //这两行必须放在主函数里
  std::uniform_int_distribution<> u(1,400);
  for (int i=1;i<=q;i++){
      int IQ=(u(rng)^lastans)%400+1;  // The IQ of the i-th friend
      int EQ=(u(rng)^lastans)%400+1;  // The EQ of the i-th friend
      int AQ=(u(rng)^lastans)%400+1;  // The AQ of the i-th friend
      lastans=solve(IQ,EQ,AQ);  // The answer to the i-th friend
      ans=(ans+1ll*lastans*Pow(1ll*seed,1ll*(q-i))%mod)%mod;
      //printf("qwq %d %d %d %d %lld\n",IQ,EQ,AQ,lastans,ans);
  }
  printf("%lld",ans);
  return 0;
}

【偏序分析+线性dp】A

【题意】

有 n 台电脑,每台有两个属性 wi 和 pi,从中选 m 台并将它们排序,使得效率$\sum_{i=1}^{m} w_i \prod_{j=1}^{i-1} p_j$最大。$1 \leq n \leq 10^5, 1 \leq m \leq min(n,20)$

【题解】

首先解决顺序问题。对于序列中相邻的两台电脑 a 和 b,a 排在 b 前的效率为${prefix\ part} + w_a \prod p_{prefix} + w_b (\prod p_{prefix})p_a + {suffix\ part}$,b 排在 a 前的效率为${prefix\ part} + w_b \prod p_{prefix} + w_a (\prod p_{prefix})p_b + {suffix\ part}$,则 ab 比 ba 效率高须满足$w_b + w_a p_b > w_b + w_a p_b$,此条件符合偏序性质,因此可以据此对 n 台电脑排序。

dp[i][j] 表示考虑了第 i ~ n 台电脑,选择了 j 台时的最大效率,则 dp[i][j] = max(选第 i 台,不选第 i 台) = max(dp[i+1][j], dp[i+1][j-1] * p[i] + w[i])。初始状态为 dp[i][0] = 0,所求答案为 dp[1][m]。

【代码】

#include <bits/stdc++.h>
using namespace std;
int n,m;
double dp[100005][25];
struct node{
  double w,q;
}a[100005];
bool cmp(node A,node B){
  return A.w*10000+A.q*B.w>B.w*10000+B.q*A.w;
}
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) scanf("%lf",&a[i].w);
  for (int i=1;i<=n;i++) scanf("%lf",&a[i].q);
  sort(a+1,a+n+1,cmp);
  for (int i=n;i>=1;i--)
    for (int j=0;j<=m;j++){
      if (j==0) dp[i][j]=dp[i+1][j];
      else dp[i][j]=max(dp[i+1][j],dp[i+1][j-1]*a[i].q/10000+a[i].w);
    }
  printf("%.15lf",dp[1][m]);
  return 0;
}

 

posted @ 2022-08-03 17:16  Maaaaax  阅读(52)  评论(0)    收藏  举报