AHUACM 第一场摸底测试复盘题解

A P4838 P哥破解密码

f[i][j]表示前i个位置结尾有j个A的合法串数量,j为0,1,2

矩阵快速幂板子

#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
const int mod = 19260817;
int f[N][3];
struct juzhen
{
    int n,m;
    int a[3][3];
    juzhen(int n=0,int m=0):n(n),m(m)
    {
        memset(a,0,sizeof(a));
    }
};
juzhen mul(juzhen &a,juzhen &b)
{
    juzhen c(a.n,b.m);
    for(int i=0;i<a.n;i++)
    {
        for(int j=0;j<b.m;j++)
        {
            for(int k=0;k<a.m;k++)
            {
                c.a[i][j]=(c.a[i][j]+1ll*a.a[i][k]*b.a[k][j]%mod)%mod;
            }
        }
    }
    return c;
}
void solve(int T)
{
    juzhen A(1,3),B(3,3),S(3,3);
    A.a[0][0]=A.a[0][1]=1;
    B.a[0][0]=1;B.a[0][1]=1;B.a[0][2]=0;
    B.a[1][0]=1;B.a[1][1]=0;B.a[1][2]=1;
    B.a[2][0]=1;B.a[2][1]=0;B.a[2][2]=0;
    S.a[0][0]=1;S.a[1][1]=1;S.a[2][2]=1;
    T--;
    while(T)
    {
        if(T&1)
        {
            S=mul(S,B);
        }
        B=mul(B,B);
        T>>=1;
    }
    A=mul(A,S);
    cout<<(A.a[0][0]+A.a[0][1]+A.a[0][2])%mod<<endl;
}
int main()
{
    int T;
    cin>>T;
    while(T --> 0)
    {
        int x;
        cin>>x;
       solve(x);
    }
    return 0;
}

B P5142 区间方差

化简后变为\(\frac{1}{n}{\sum{x_i^2}}-\bar{x}^2\)

用线段树维护区间和 以及 区间平方和 ,支持单点修改区间查询

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 1e9+7;
const int N = 3e5 + 10;
struct linetree
{
    LL sum1,sum2;
}t[N<<2];
LL a[N];
void pushup(int p)
{
    t[p].sum1 = (t[p*2].sum1 + t[p*2+1].sum1)%mod;
    t[p].sum2 = (t[p*2].sum2 + t[p*2+1].sum2)%mod;
}
void build(int p,int l,int r)
{
    if(l==r)
    {
        t[p].sum1 = a[l];
        t[p].sum2 = a[l] * a[l]%mod;
        return;
    }
    int mid = (l + r) >> 1;
    build(p*2, l, mid);     
    build(p*2+1, mid+1, r);
    pushup(p);
}
void update(int p,int l,int r,int x,LL v)
{
    if(l==r)
    {
        t[p].sum1=v;
        t[p].sum2=v*v%mod;
        return;
    }
    int mid = (l + r) >> 1;
    if(x <= mid)
        update(p*2, l, mid, x, v);
    else
        update(p*2+1, mid+1, r, x, v);
    pushup(p);
}

LL find1(int p,int l,int r,int L,int R)
{
    if(L <= l && r <= R)
        return t[p].sum1;
    int mid = (l + r) >> 1;
    LL sum = 0;
    if(L <= mid)
        sum += find1(p*2, l, mid, L, R);
    if(R > mid)
        sum += find1(p*2+1, mid+1, r, L, R);
    return sum % mod;
}
LL find2(int p,int l,int r,int L,int R)
{
    if(L <= l && r <= R)
        return t[p].sum2;
    int mid = (l + r) >> 1;
    LL sum = 0;
    if(L <= mid)
        sum += find2(p*2, l, mid, L, R);
    if(R > mid)
        sum += find2(p*2+1, mid+1, r, L, R);
    return sum % mod;
}
LL qpow(LL a,LL b=mod-2)
{
    LL s=1;
    while(b)
    {
        if(b&1)
            s=s*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return s;
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int c,x,y;
        cin>>c>>x>>y;
        if(c==1)
        {
            update(1,1,n,x,y);
        }else
        {
            LL ans=0;
            LL sum1=find1(1,1,n,x,y);
            LL sum2=find2(1,1,n,x,y);
            ans=qpow(y-x+1)*sum2%mod;
            sum1=qpow(y-x+1)*sum1%mod;
            ans=(ans-sum1*sum1%mod+mod)%mod;
            cout<<ans<<endl;
        }
    }
    return 0;
}

C [NOIP 2013 提高组] 货车运输

从u到v的路径上最小值最大的路径一定在最大生成树上可以找到。

证明:假设最大生成树上不存在能使最小值最大的路径,设最小值对应的边为e1,两边的点为u1,u2,那么在最大生成树上的u1,u2之间连一条边,最大生成树上路径的最小值一定在换上,此时将其切掉会产生一个更大的生成树,不符合最大生成树定义。

求个最大生成树+倍增lca即可

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
struct Dsu
{
  int n;
  int fa[maxn];
  int find(int x) {
    if (fa[x] != x) fa[x] = find(fa[x]);
    return fa[x];
  }
  void merge(int x, int y) {
    int fx = find(x), fy = find(y);
    if (fx != fy) fa[fx] = fy;
  }
  bool check(int x, int y) {
    return find(x) == find(y);
  }
  void init(int n) {
    this->n = n;
    for (int i = 1; i <= n; i++) fa[i] = i;
  }
}D;
struct Edge {
   int x,y,w;
}a[maxn];
bool cmp(Edge a, Edge b) {
    return a.w > b.w;
}
vector<pair<int,int>>e[maxn];
int f[maxn][20],fa[maxn][20],dep[maxn];
void dfs(int u)
{
  for(auto i:e[u])
  {
    int v=i.first;
    if(v==fa[u][0]) continue;
    fa[v][0] = u;
    dep[v]=dep[u]+1;
    f[v][0] = i.second;
    dfs(v);
  }
}
void init(int n)
{
  for(int j=1;j<20;j++)
  {
    f[0][j]=1e9;
    for(int i=1;i<=n;i++)
    {
      fa[i][j] = fa[fa[i][j-1]][j-1];
      f[i][j] = min(f[i][j-1], f[fa[i][j-1]][j-1]);
    }
  }
}
int lca(int x,int y)
{
  if(dep[x]<dep[y])swap(x,y);
  int d = dep[x] - dep[y];
  int ans=1e9;
  for(int i=0;i<20;i++)
  {
    if(d&(1<<i)) ans=min(ans, f[x][i]),x = fa[x][i];
  }
  for(int i=19;i>=0;i--)
  {
    if(fa[x][i]!=fa[y][i])
    {
      ans=min(ans, min(f[x][i], f[y][i]));
      x = fa[x][i];
      y = fa[y][i];
    }
  }
  return x == y ? ans : min(ans, min(f[x][0], f[y][0])); 
}
int main()
{
  int n,m;
  cin>>n>>m;
  for(int i=1;i<=m;i++) 
  {
    cin>>a[i].x>>a[i].y>>a[i].w;
  }
  sort(a+1, a+m+1, cmp);
  D.init(n);
  for(int i=1;i<=m;i++)
  {
    if(D.check(a[i].x,a[i].y))continue;
    e[a[i].x].push_back({a[i].y, a[i].w});
    e[a[i].y].push_back({a[i].x, a[i].w});
    D.merge(a[i].x, a[i].y);
  }
  f[0][0]=1e9;
  for(int i=1;i<=n;i++)
  {
    if(!dep[i]) {
      f[i][0]=1e9;
      dfs(i);
    }
  }
  init(n);
  int q;
  cin>>q;
  while(q--) {
    int x,y;
    cin>>x>>y;
    if(!D.check(x,y)) {
      cout << -1 << endl;
      continue;
    }
    cout << lca(x,y) << endl;
  }

  return 0;
}

D P2804 神秘数字

数位dp的板子题,三年没做过了,也算是当复健吧

整体还是一个递归的思想。

0对答案没有贡献,不需要考虑

计算[0,A1A2A3……An] 中每一个数字的出现次数,可以将其分成高位贡献和其他位的贡献,也就是下面三个部分:

(1)[0,A1 00……0)中各位数位数字出现的总和

(2)A2A3……An个A1 数字

(3)[0,A2A3……An] 中各位数字的总和

(1)部分:对于数位i,其出现次数为:A1*f[n-1]+tenn-1

(2)部分 :A2A3……An+1次的数位A1

(3)部分 递归解决

#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long

const LL mod = 1e9+7;
LL f[21],ten[21];
void init()
{
  ten[0]=1;
  for(int i=1;i<=18;i++)
  {
    ten[i]=ten[i-1]*10;
    f[i]=10*f[i-1]+ten[i-1];
  }
}
LL cnt[10],num[21],ct[10];
void solve(LL x)
{
  LL n=0,y=x;
  while(y)
  {
    num[++n]=y%10;
    y/=10;
  }
  for(int i=0;i<10;i++)
    cnt[i]=0;
  for(int i=n;i>=1;i--)
  {
    for(int j=0;j<10;j++)
    {
      cnt[j]+=f[i-1]*num[i];
    }
    for(int j=1;j<num[i];j++)
    {
      cnt[j]+=ten[i-1];
    }
    x-=num[i]*ten[i-1];
    cnt[num[i]]+=x+1;
  }
}
int main()
{
  init();
  solve(9);
  for(int i=0;i<10;i++)
    cout<<i<<" : "<<cnt[i]<<endl;
  return 0;
}

E P2326 AKN’s PPAP

求数列中两数与的最大值

按位比较,初始时所有数都在集合中,从高位向低位扫,如果这一位有值的数不超过1个,直接跳过,恰好两个,得出答案;大于2个,这一位不存在的数直接out

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 200005;
LL a[maxn],ans;
const LL mod = 92084931;
void merge_sort(int l,int r)
{
  if(l==r) return;
  int mid=(l+r)>>1;
  merge_sort(l,mid);
  merge_sort(mid+1,r);
  vector<LL> b, c;
  for(int i=l;i<=mid;i++) b.push_back(a[i]);
  for(int i=mid+1;i<=r;i++) c.push_back(a[i]);
  int i=0,j=0;
  while(i<b.size() && j<c.size())
  {
    if(b[i]<c[j]) 
    {
      a[l+i+j]= b[i];
      i++;
    }
    else
    {
      ans+=b.size()-i;
      a[l+i+j]= c[j];
      j++;
    }
  }
  while(i<b.size())
  {
    a[l+i+j]= b[i];
    i++;
  }
  while(j<c.size())
  {
    a[l+i+j] = c[j];
    j++;
  }
}

int main()
{
  int n,M;
  cin>>n>>M;
  for(int i=1;i<=n;i++)
  {
    cin>>a[i];
  }
  for(int i=1;i<=n;i++)
  {
    a[i]-=M;
    a[i]+=a[i-1];
  }
  merge_sort(0,n);
  cout<<(1ll*n*(n+1)/2-ans+mod)%mod<<endl;
  return 0;
}

F P2804 神秘数字

将数列a中元素全减去M,条件就变成了区间和大于0

此时再做个前缀和,答案就变成了统计前缀和个数

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 200005;
LL a[maxn],ans;
const LL mod = 92084931;
void merge_sort(int l,int r)
{
  if(l==r) return;
  int mid=(l+r)>>1;
  merge_sort(l,mid);
  merge_sort(mid+1,r);
  vector<LL> b, c;
  for(int i=l;i<=mid;i++) b.push_back(a[i]);
  for(int i=mid+1;i<=r;i++) c.push_back(a[i]);
  int i=0,j=0;
  while(i<b.size() && j<c.size())
  {
    if(b[i]<c[j]) 
    {
      a[l+i+j]= b[i];
      i++;
    }
    else
    {
      ans+=b.size()-i;
      a[l+i+j]= c[j];
      j++;
    }
  }
  while(i<b.size())
  {
    a[l+i+j]= b[i];
    i++;
  }
  while(j<c.size())
  {
    a[l+i+j] = c[j];
    j++;
  }
}

int main()
{
  int n,M;
  cin>>n>>M;
  for(int i=1;i<=n;i++)
  {
    cin>>a[i];
  }
  for(int i=1;i<=n;i++)
  {
    a[i]-=M;
    a[i]+=a[i-1];
  }
  merge_sort(0,n);
  cout<<(1ll*n*(n+1)/2-ans+mod)%mod<<endl;
  return 0;
}
posted @ 2025-07-13 19:34  arthalter  阅读(6)  评论(0)    收藏  举报