2022-9-26

T1

扩散

明显可以二分答案,也可以用最小生成树去做。考试时写的最小生成树,每两个点连一条边权为这两个点的曼哈顿距离。每次找最小的距离,\(\div 2+1\)后更新\(ans\)(因为两个点都会扩散)

代码

#include<bits/stdc++.h>
#define MAXN 60
#define int long long
using namespace std;
int n,tot=0,ans=0;
int father[MAXN];
int fa(int data)
{
    if(father[data]==data)return data;
    return father[data]=fa(father[data]);
}
struct point
{
    int x,y;
}p[MAXN];
struct edge
{
    int l,r,w;
}ed[3000];
bool cmp(const edge a,const edge b)
{
    return a.w<b.w;
}
signed main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%lld",&p[i].x,&p[i].y);
        father[i]=i;
        for(int j=1;j<i;j++)
        {
            ed[++tot].l=j;
            ed[tot].r=i;
            ed[tot].w=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y)-1;
        }
    }
    sort(ed+1,ed+tot+1,cmp);
    int cnt=0;
    for(int i=1;i<=tot;i++)
    {
        if(fa(ed[i].l)==fa(ed[i].r))continue;
        father[fa(ed[i].l)]=father[fa(ed[i].r)];
        ans=max(ans,ed[i].w/2+1);
        cnt++;
        if(cnt==n-1)break;
    }
    printf("%lld",ans);
    return 0;
}

T2

树的统计

树链剖分模板题,不多赘述。

T3

佳佳的Fibonacci

问题描述:

佳佳对数学,尤其对数列十分感兴趣。在研究完 \(Fibonacci\) 数列后,他创造出许多稀奇古怪的数列。例如用\(S(n)\)表示 \(Fibonacci\)\(n\)项和 \(mod\) \(m\) 的值,即\(S(n)=(F1+F2+…+Fn)mod\) \(m\),其中 \(F1=F2=1\)\(Fi=Fi−1+Fi−2\)。可这对佳佳来说还是小菜一碟。终于,她找到了一个自己解决不了的问题。用\(T(n)=(F1+2F2+3F3+…+nFn)mod\) \(m\) 表示 \(Fibonacci\) 数列前\(n\)项变形后的和 \(mod\) \(m\) 的值。现在佳佳告诉你了一个\(n\)\(m\),请求出\(T(n)\)的值。

输入格式

共一行,包含两个整数 \(n\)\(m\)

输出格式

共一行,输出 \(T(n)\) 的值。

数据范围

1 ≤ \(n\)\(m\)\(2 ^ {31} − 1\)

思路:

看题目已经给出了递推式,数据范围很大,考虑矩阵加速递推。

易得递推式为\(T_i=T_{i-1}+iF_i\)

注意到这个递推式有点特殊,包含\(n\)\(F_n\)的乘积项。如果普通地把\(n\)\(F_n\)放到状态矩阵中,不管怎么设计转移矩阵都不会产生乘积项。因此我们可以把乘积项\(nF_n\)也放入矩阵中。
如何转移 \(nF_n\) ? 推导思路如下:

\[\begin{aligned} (i+1)F_{i+1}&=iF_{i+1}+F_{i+1}\\ &=i(F_i+F_{i-1})+F_{i-1}+F_i\\ &=iF_i+iF_{i-1}+F_{i-1}+F_i\\ &=iF_i+(i-1)F_{i-1}+F_{i-1}+F_{i-1}+F_i\\ &=iF_i+(i-1)F_{i-1}+2F_{i-1}+F_i \end{aligned} \]

因此我们需要记录\(iF_i、(i-1)F_{i-1}、F_{i-1}、F_i\),再加上\(T_{i-1}\)\(5\)个信息。
状态矩阵为

\[\begin{bmatrix} T_{i-1} & F_i & F_{i-1} & iF_i & (i-1)F_{i-1}\end{bmatrix} \]

它要转移到

\[\begin{bmatrix} T_i & F_{i+1} & F_i & (i+1)F_{i+1} & iF_i\end{bmatrix} \]

可得转移矩阵为

\[\begin{bmatrix} 1 & 0 & 0 & 0 & 0\\ 0 & 1 & 1 & 1 & 0\\ 0 & 1 & 0 & 2 & 0\\ 1 & 0 & 0 & 1 & 1\\ 0 & 0 & 0 & 1 & 0 \end{bmatrix}\]

状态矩阵初始化为\(\begin{bmatrix} 0 & 1 & 0 & 1 & 0\end{bmatrix}\),快速幂求\(n\)次方即可。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,mod;
struct matrix
{
    int mat[10][10],n,m;
    matrix(int a,int b)
    {
        n=a,m=b;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                mat[i][j]=0;
    }
    void set_i()
    {
        for(int i=1;i<=n;i++)
            mat[i][i]=1;
    }
    matrix operator *(matrix b)
    {
        matrix ans(n,b.m);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=b.m;j++)
                for(int k=1;k<=m;k++)
                    ans.mat[i][j]=(ans.mat[i][j]+mat[i][k]*b.mat[k][j]%mod)%mod;
        return ans;
    }
};
matrix qpow(matrix a,int b)
{
    matrix ans(a.n,a.m);
    ans.set_i();
    while(b)
    {
        if(b&1)ans=ans*a;
        a=a*a;
        b>>=1;
    }
    return ans;
}
signed main()
{
    scanf("%lld%lld",&n,&mod);
    matrix st(1,5);
    st.mat[1][1]=0;st.mat[1][3]=st.mat[1][5]=0;
    st.mat[1][2]=st.mat[1][4]=1;
    matrix a(5,5);
    a.mat[1][1]=a.mat[2][2]=a.mat[2][3]=a.mat[2][4]=a.mat[3][2]=a.mat[4][1]=a.mat[4][4]=a.mat[4][5]=a.mat[5][4]=1;
    a.mat[3][4]=2;

    st=st*qpow(a,n);
    printf("%lld",st.mat[1][1]%mod);
    return 0;
}

T4

[CQOI2009]中位数

题目描述

给出 \(1,2,...,n\) 的一个排列,统计该排列有多少个长度为奇数的连续子序列的中位数是 \(b\)。中位数是指把所有元素从小到大排列后,位于中间的数。

输入格式

第一行为两个正整数 \(n\)\(b\),第二行为 \(1,2,...,n\) 的排列。

输出格式

输出一个整数,即中位数为 \(b\) 的连续子序列个数。

提示

对于 \(30\%\) 的数据中,满足 \(n \le 100\)

对于 \(60\%\) 的数据中,满足 \(n \le 1000\)

对于 \(100\%\) 的数据中,满足 \(n \le 100000,1 \le b \le n\)

思路

把数组中比 \(b\) 大的数设为 \(1\) ,比 \(b\) 小的数设为 \(-1\) ,这样如果 \(b\) 为一段连续子序列的中位数,那么这段子序列除 \(b\) 外其他数的和一定为\(0\)。因此我们先从 \(b\) 所在的位置开始往左边扫一遍,在扫的过程中累加元素的值,并开一个 \(map\) 存有多少个节点到 \(b\) 的和是这个值。再从 \(b+1\) 开始从左往右扫,跟第一遍一样累加,每累加一个节点就\(ans+=map[-sum]\),最后得到答案。

代码

#include<bits/stdc++.h>
#define MAXN 100010
using namespace std;
int n,b,pos,a[MAXN],ans=0,sum=0;
map<int,int> mp;
int main()
{
	scanf("%d%d",&n,&b);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]==b)pos=i;
		else if(a[i]<b)a[i]=-1;
		else a[i]=1;
	}
	for(int i=pos-1;i>=1;i--)
	{
		sum+=a[i];
		if(sum==0)ans++;
		mp[sum]++;
	}
	sum=0;
	for(int i=pos+1;i<=n;i++)
	{
		sum+=a[i];
		if(sum==0)ans++;
		ans+=mp[0-sum];
	}
	printf("%d",++ans);
    return 0;
}
 posted on 2022-09-26 20:07  hu_led  阅读(16)  评论(0编辑  收藏  举报