Noip yjm 题解

Noip yjm 题解

T1 分治

因为题目描述分为前半段和后半段,且长度满足\(n=2^k\),所以不难想到分治。

然后分别判断是否满足三个条件之一,分别统计如果把当前串前半段变为全是ch,或后半段全变为ch所需代价,递归到下一层进行统计

int sol(int ch,int l,int r)
{
    if(l+1==r) return s[l]!=ch;//满足条件1,只有一个字母ch
    int mid=(l+r)>>1,cl=0,cr=0;
    per(i,l,mid-1) cl+=(s[i]!=ch);//统计将前半段变成全是ch的代价
    per(i,mid,r-1) cr+=(s[i]!=ch);//统计将后半段变成全是ch的代价
    return min(cl+sol(ch+1,mid,r),cr+sol(ch+1,l,mid));//分治求解
}

代码

#include <bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i(a);i<=b;++i)
const int N=131080;
char s[N];
int sol(int ch,int l,int r)
{
    if(l+1==r) return s[l]!=ch;
    int mid=(l+r)>>1,cl=0,cr=0;
    per(i,l,mid-1) cl+=(s[i]!=ch);
    per(i,mid,r-1) cr+=(s[i]!=ch);
    return min(cl+sol(ch+1,mid,r),cr+sol(ch+1,l,mid));
}
int main ()
{
    int t,n;
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n>>s+1;
        cout<<sol('a',1,n+1)<<'\n';
    }
	return 0;
}

T2 思维+图的遍历

如果一支军队在相同天气下能打赢另一只军队,则从战败方向战胜方引一条有向边。

从某一个地图中的战力值最大的点开始遍历,能遍历到的都是符合题意的

(战力值最大的一定能赢得所有的点,他能到达的点为能(直接或间接)战胜它的点)所以被遍历到的点都能获得总冠军

代码

#include <bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i(a);i<=b;++i)
const int N=1e5+10;
struct aaa{
    int x,y,id;
}a[N];
vector<int>mp[N];
bitset<N>ck;
void addd(int x,int y) { mp[x].push_back(y);}
bool cmp1(aaa f,aaa s) { return f.x<s.x;}
bool cmp2(aaa f,aaa s) { return f.y<s.y;}
void sol(int x)
{
    if(ck[x]) return;
    ck[x]=1;
    per(i,0,(int)mp[x].size()-1) sol(mp[x][i]);
}
int main ()
{
    int n,t,rt;
    cin>>t;
    while(t--)
    {
        cin>>n;
        per(i,1,n) scanf("%d",&a[i].x);
        per(i,1,n) scanf("%d",&a[i].y),a[i].id=i,mp[i].clear();
        sort(a+1,a+n+1,cmp1);
        per(i,1,n-1) addd(a[i].id,a[i+1].id);
        rt=a[n].id;
        sort(a+1,a+n+1,cmp2);
        per(i,1,n-1) addd(a[i].id,a[i+1].id);
        ck.reset();
        sol(a[n].id);
        sol(rt);
        per(i,1,n)
        {
            if(ck[i]) printf("1");
            else printf("0");
        }
        puts("");
    }
	return 0;
}

T3 矩阵快速幂

30tps:暴力算法,O(n) \(dp[i][j]\)表示当前用了\(i\)个篮球,占了\(j\)个位置的方案数

优化减去一个维度变成一维\(dp[i]\)表示占用i个位置的方案数,如果上一个操作加入的是一个魔法篮球,\(dp[i]+=dp[i-1]\);如果上一个操作加入的是由一个魔法篮球分解得到的普通篮球,\(dp[i]+=dp[i-m]\),所以转移方程为\(dp[i]=dp[i-1]+dp[i-m]\)

考虑优化,引入矩阵快速幂

状态矩阵A={\(dp[0],dp[1],...,dp[m-1]\)}

构造转移矩阵B,使{\(dp[0],dp[1],...,dp[m-1]\)}*B={\(dp[1],dp[2],...,dp[m]\)}

所以A*Bn\(dp[n]\)即为答案,即\(A[1][1]\)

构建的矩阵B的主要目的是将A每位都向前移动,所以\(B[i+1][i]=1,B[1][m]=1,B[m][m]=1\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
#define ll long long int
struct matrix{
    ll x[200][200];
};
ll n,m;
matrix mul(const matrix &a, const matrix &b)
{
    matrix ans;
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=m;j++)
        {
            ans.x[i][j]=0;
            for(int k=1;k<=m;k++)
            {
                ans.x[i][j] += a.x[i][k] * b.x[k][j];
                ans.x[i][j] %= mod;
            }
        }
    }
    return ans;
}
matrix qpow(matrix a,ll b)
{
    matrix ans;
    for(int i=1;i<=m;i++)ans.x[i][i]=1;
    while(b)
    {
        if(b&1)ans = mul(ans, a);
        a = mul(a, a);
        b>>=1;
    }
    return ans;
}
int main()
{
    cin>>n>>m;
    if(n<m){cout<<1<<endl;return 0;}
    matrix base;
    for(int i=1;i<m;i++) base.x[i+1][i] = 1;
    base.x[1][m] = base.x[m][m] = 1;
    base = qpow(base, n-m);
    matrix ans;
    for(int i=1;i<m;i++)ans.x[1][i]=1;
    ans.x[1][m] = 2;
    ans = mul(ans, base);
    cout<<ans.x[1][m]<<endl;
    return 0;
}

T4 期望概率dp

\(h[i][j]\) 表示 \(n=i,k=j\) 的结果

每个质因子的贡献实际上是独立的,反应在次数上就是相乘的关系

因此,每个质因子分别贡献,总答案为乘积

现在对于每个质因子分别算即可

对于 p,设 \(f[i][j]\)表示 \(n=p^i,k=j\) 的结果,那么 \(f[i][j]={1\over i+1}\sum_{k=0}^j f[k][j−1]\)
https://www.cnblogs.com/bcoier/p/11621145.html

代码

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int mod = 1e9 + 7;
int f[60][10005];

int qpow(int p, int q)
{
    return (q & 1 ? p : 1) * (q ? qpow(p * p % mod, q / 2) : 1) % mod;
}

int solve(int p, int i, int j)
{
    if (f[i][j])
        return f[i][j];
    if (i == 0)
        return f[i][j] = 1;
    if (j == 0)
        return f[i][j] = qpow(p, i);
    f[i][j] = 0;
    for (int k = 0; k <= i; k++)
        f[i][j] += solve(p, k, j - 1);
    return f[i][j] = f[i][j] % mod * qpow(i + 1, mod - 2) % mod;
}

signed main()
{
    ios::sync_with_stdio(false);

    int n, k;
    cin >> n >> k;
    int ans = 1;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0)
        {
            int cnt = 0;
            while (n % i == 0)
                n /= i, ++cnt;
            memset(f, 0, sizeof f);
            ans *= solve(i, cnt, k);
            ans %= mod;
        }
    memset(f, 0, sizeof f);
    if (n > 1)
        ans *= solve(n, 1, k);
    ans %= mod;
    cout << ans << endl;
}
posted @ 2022-10-14 16:10  f2021yjm  阅读(41)  评论(0编辑  收藏  举报