“科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛题解

题目链接

按难度顺序

F. 排列计算

题意:
有 一 个 长 度 为 n 的 序 列 a , m 次 询 问 有一个长度为n的序列a,m次询问 nam
每 次 询 问 一 个 区 间 和 , 把 每 次 区 间 和 加 起 来 就 是 最 后 结 果 每次询问一个区间和,把每次区间和加起来就是最后结果
问 最 后 结 果 最 大 是 多 少 问最后结果最大是多少
题解:
想 要 结 果 最 大 , 肯 定 是 最 大 的 数 取 的 次 数 最 多 即 可 想要结果最大,肯定是最大的数取的次数最多即可
所 以 直 接 差 分 , 将 给 出 的 区 间 位 置 计 数 , 看 每 个 位 置 出 现 的 次 数 所以直接差分,将给出的区间位置计数,看每个位置出现的次数
然 后 排 序 一 下 , 大 的 给 大 数 , 小 的 给 小 数 然后排序一下,大的给大数,小的给小数
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
ll a[maxn];
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        int l,r;
        cin>>l>>r;
        a[l]++,a[r+1]--;
    }
    for(int i=1;i<=n;i++)a[i]+=a[i-1];
    sort(a+1,a+1+n);
    ll ans=0;
    for(ll i=1;i<=n;i++){
        ans+=i*a[i];
    }
    cout<<ans;
    return 0;
}

B. 伤害计算

题意:
给 一 个 字 符 串 , 是 一 个 加 法 表 达 式 给一个字符串,是一个加法表达式
每 个 部 分 可 能 是 一 个 数 , 也 可 能 是 一 个 n d x 每个部分可能是一个数,也可能是一个ndx ndx
n d x 表 示 n 个 x 面 的 骰 子 ndx表示n个x面的骰子 ndxnx
数 的 贡 献 是 数 自 身 , 骰 子 的 贡 献 是 他 的 期 望 数的贡献是数自身,骰子的贡献是他的期望
题解:
这 道 题 其 实 表 面 一 看 算 法 肯 定 不 难 这道题其实表面一看算法肯定不难
骰 子 的 期 望 其 实 就 是 1 − x 的 和 除 x 骰子的期望其实就是1-x的和除x 1xx
化 简 一 下 就 是 ( x + 1 ) / 2 , n 个 筛 子 乘 个 n 化简一下就是(x+1)/2,n个筛子乘个n x+1/2nn
这 道 题 比 较 难 的 是 怎 么 读 数 这道题比较难的是怎么读数
看 到 有 d a l a o 用 p y 的 s p l i t 轻 松 解 决 , 但 我 只 会 c + + 看到有dalao用py的split轻松解决,但我只会c++ dalaopysplitc++
我 用 的 操 作 就 是 每 次 读 一 个 数 a 表 示 个 数 , b 表 示 面 数 我用的操作就是每次读一个数a表示个数,b表示面数 ab
读 数 就 是 读 到 一 个 数 加 一 位 , 然 后 直 到 读 的 不 是 数 为 止 读数就是读到一个数加一位,然后直到读的不是数为止
从 0 到 字 符 串 结 束 , 每 次 读 , 读 完 a 后 观 察 一 下 从0到字符串结束,每次读,读完a后观察一下 0a
如 果 下 一 位 是 个 d , 则 说 明 要 读 b , 然 后 按 骰 子 计 算 如果下一位是个d,则说明要读b,然后按骰子计算 db
如 果 当 前 读 到 字 符 串 结 束 或 者 下 一 位 是 个 加 号 如果当前读到字符串结束或者下一位是个加号
这 说 明 这 一 项 就 是 一 个 单 独 的 数 , 直 接 加 上 即 可 这说明这一项就是一个单独的数,直接加上即可
然 后 这 道 题 输 出 也 是 一 个 坑 点 , 如 果 整 数 直 接 输 出 , 否 则 保 留 一 位 , 需 要 判 断 一 下 然后这道题输出也是一个坑点,如果整数直接输出,否则保留一位,需要判断一下
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};


int main()
{
    //ios::sync_with_stdio(false);
    //cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
   string s;
    cin>>s;
    double ans=0;
    int i=0;
    while(i<s.length()){
        double a=0,b=0;
        while(isdigit(s[i])&&i<s.length())a=a*10+s[i]-'0',i++;
        if(i==s.length()){ans+=a;break;}
        if(s[i]!='d'){ans+=a;i++;continue;}
        i++;
        while(isdigit(s[i])&&i<s.length())b=b*10+s[i]-'0',i++;
        i++;
        ans+=a*(b+1)/2;
    }
    ll res=ans;
    if(res==ans)cout<<res;
    else printf("%.1lf",ans);
    return 0;
}


A.张老师和菜哭武的游戏

题意:
一 共 n 个 数 , 首 先 张 老 师 和 菜 哭 武 分 别 选 数 a 和 b 放 入 集 合 一共n个数,首先张老师和菜哭武分别选数a和b放入集合 nab
张 老 师 先 手 , 每 次 选 一 个 数 x 张老师先手,每次选一个数x x
这 个 数 必 须 是 集 合 里 任 意 两 个 数 的 和 或 差 这个数必须是集合里任意两个数的和或差
取 不 到 数 的 人 输 , 问 张 老 师 是 否 能 赢 取不到数的人输,问张老师是否能赢
题解:
根 据 题 意 , 多 次 相 互 求 差 和 和 , 很 明 显 根据题意,多次相互求差和和,很明显
这 是 一 个 g c d 操 作 的 分 部 这是一个gcd操作的分部 gcd
所 以 你 只 要 找 n 个 数 里 有 多 少 个 数 符 合 这 个 数 所以你只要找n个数里有多少个数符合这个数 n
由 于 初 始 数 是 a 和 b , 符 合 这 个 数 一 定 是 g c d ( a , b ) 的 倍 数 由于初始数是a和b,符合这个数一定是gcd(a,b)的倍数 abgcd(a,b)
如 果 有 奇 数 个 说 明 张 老 师 会 拿 走 最 后 一 个 , 说 明 赢 了 如果有奇数个说明张老师会拿走最后一个,说明赢了
否 则 就 是 输 了 否则就是输了
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
 
ll a[maxn];
 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin>>t;
    while(t--){
        int n,a,b;
        cin>>n>>a>>b;
        if((n/__gcd(a,b))&1)cout<<"Yes"<<endl;
        else cout<<"No"<<endl;
    }
    return 0;
}

D.车辆调度

题意:
有 一 张 w ∗ h 的 图 , 一 个 或 多 辆 车 有一张w*h的图,一个或多辆车 wh
每 辆 车 可 以 进 行 如 下 操 作 每辆车可以进行如下操作
可 以 向 上 下 左 右 任 意 一 个 方 向 行 驶 边 界 或 者 撞 到 另 外 一 辆 车 或 障 碍 物 可以向上下左右任意一个方向行驶边界或者撞到另外一辆车或障碍物
每 个 操 作 一 分 钟 每个操作一分钟
图 中 存 在 一 个 或 多 个 目 标 点 图中存在一个或多个目标点
问 你 是 否 可 能 在 k 分 钟 的 时 候 , 有 任 一 一 辆 车 到 任 一 一 个 目 标 点 问你是否可能在k分钟的时候,有任一一辆车到任一一个目标点 k
题解:
w , h < = 10 , k < = 5 w,h<=10,k<=5 w,h<=10,k<=5
这 道 题 数 据 非 常 小 , 直 接 用 D F S 回 溯 暴 力 每 一 时 刻 的 状 态 这道题数据非常小,直接用DFS回溯暴力每一时刻的状态 DFS
由 于 不 是 碰 到 目 标 点 就 能 停 止 , 所 以 目 标 地 相 当 于 空 地 由于不是碰到目标点就能停止,所以目标地相当于空地
把 目 标 地 变 成 空 地 , 然 后 用 s e t 维 护 一 下 目 标 点 把目标地变成空地,然后用set维护一下目标点 set
接 下 来 就 用 D F S 直 接 暴 力 每 分 钟 的 操 作 接下来就用DFS直接暴力每分钟的操作 DFS
枚 举 任 何 一 辆 车 往 四 个 方 向 的 某 一 个 走 到 头 枚举任何一辆车往四个方向的某一个走到头
然 后 D F S 下 一 分 钟 , 直 到 有 一 辆 车 到 目 标 点 或 者 超 出 给 定 时 间 然后DFS下一分钟,直到有一辆车到目标点或者超出给定时间 DFS
每 次 车 移 动 后 要 更 新 一 下 车 的 新 位 置 和 旧 位 置 每次车移动后要更新一下车的新位置和旧位置
因 为 别 的 车 会 因 为 这 个 车 停 止 因为别的车会因为这个车停止
如 果 遇 到 某 一 个 时 刻 某 辆 车 到 目 标 点 , 直 接 输 出 y e s 然 后 e x i t ( 0 ) 如果遇到某一个时刻某辆车到目标点,直接输出yes然后exit(0) yesexit(0)
否 则 就 输 出 n o 否则就输出no no
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int n,m,k;
char g[20][20];
set<pii> e;
vector<pii> s;
bool ok(int x,int y){
    if(x>=1&&x<=n&&y>=1&&y<=m&&g[x][y]=='.')return 1;
    return 0;
}
void dfs(int cur){
    if(cur==k)return;
    for(auto &i:s)
        for(int p=0;p<4;p++){
            int cx=i.fi,cy=i.se;
            int x=i.fi,y=i.se;
            int dx=x+dir[p][0];
            int dy=y+dir[p][1];
            while(ok(dx,dy))x=dx,y=dy,dx+=dir[p][0],dy+=dir[p][1];
            if(e.count(mp(x,y))){cout<<"YES";exit(0);}
            swap(g[cx][cy],g[x][y]);
            i.fi=x,i.se=y;
            dfs(cur+1);
            i.fi=cx,i.se=cy;
            swap(g[cx][cy],g[x][y]);
        }
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>m>>n>>k;
    for(int i=1;i<=n;i++)cin>>g[i]+1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            if(g[i][j]=='R')s.pb(mp(i,j));
            if(g[i][j]=='D')e.insert(mp(i,j)),g[i][j]='.';
        }
    dfs(0);
    cout<<"NO"<<endl;
    return 0;
}


E.弦

题意:
一 个 圆 上 有 2 n 个 点 , 找 出 n 条 弦 不 相 交 的 概 率 一个圆上有2n个点,找出n条弦不相交的概率 2nn
题解:
先 找 2 n 个 点 不 相 交 有 多 少 种 , 作 为 分 子 先找2n个点不相交有多少种,作为分子 2n
通 过 打 表 或 者 推 一 下 规 律 可 以 发 现 结 果 应 该 是 f [ i ] ∗ f [ n − i − 1 ] 的 累 加 通过打表或者推一下规律可以发现结果应该是f[i]*f[n-i-1]的累加 f[i]f[ni1]
如 果 学 习 过 卡 特 兰 数 的 , 一 看 这 应 该 是 个 卡 特 兰 数 的 递 推 式 如果学习过卡特兰数的,一看这应该是个卡特兰数的递推式
所 以 不 用 多 说 了 , 这 个 分 子 是 个 卡 特 兰 数 所以不用多说了,这个分子是个卡特兰数
分 母 很 好 算 , 每 次 从 2 ∗ n 个 数 里 选 两 个 分母很好算,每次从2*n个数里选两个 2n
然 后 从 2 ∗ n − 2 里 再 选 两 个 , 直 到 选 完 为 止 然后从2*n-2里再选两个,直到选完为止 2n2
但 是 这 样 会 出 现 重 复 状 态 , 就 需 要 再 除 以 n ! 但是这样会出现重复状态,就需要再除以n! n!
卡 特 兰 数 K [ i ] = C ( 2 n , n ) / ( n + 1 ) 卡特兰数K[i]=C(2n,n)/(n+1) K[i]=C(2n,n)/(n+1)
和 分 母 放 一 起 , 化 简 , 即 可 得 到 2 n / ( n + 1 ) ! 和分母放一起,化简,即可得到2^n/(n+1)! 2n/(n+1)!
由 于 数 比 较 大 需 要 取 余 , 记 得 用 一 下 费 马 小 求 逆 元 就 可 以 了 由于数比较大需要取余,记得用一下费马小求逆元就可以了
AC代码

在这里插入代码片`/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

ll Pow(ll a, ll b){
	ll ans = 1;
	while(b > 0){
		if(b & 1){
			ans = ans * a % mod;
		}
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
//逆元
ll inv(ll b){
    return Pow(b,mod-2);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n;
    cin>>n;
    ll a=Pow(2,n),b=1;
    for(ll i=1;i<=n+1;i++)b=b*i%mod;
    ll ans=a*inv(b)%mod;
    cout<<ans;
    return 0;
}
`

C. 张老师的旅行

题意:
共 有 n 个 点 , 所 有 点 在 一 条 直 线 上 , 有 一 个 坐 标 p i 共有n个点,所有点在一条直线上,有一个坐标p_i n线pi
张 老 师 每 次 走 一 公 里 需 要 花 费 一 分 钟 张老师每次走一公里需要花费一分钟
张 老 师 需 要 在 t i 之 前 到 达 i 点 , 然 后 每 个 点 都 要 走 一 遍 张老师需要在t_i之前到达i点,然后每个点都要走一遍 tii
有 一 个 t i 为 0 的 点 是 张 老 师 的 起 点 有一个t_i为0的点是张老师的起点 ti0
问 张 老 师 最 少 需 要 多 长 时 间 完 成 问张老师最少需要多长时间完成
完 成 不 了 输 出 − 1 完成不了输出-1 1
题解:
n < = 1 e 3 n<=1e3 n<=1e3
感 觉 一 开 始 看 这 道 题 比 较 像 二 分 答 案 , 但 是 c h e c k 函 数 不 好 写 感觉一开始看这道题比较像二分答案,但是check函数不好写 check
然 后 观 察 这 个 n 不 是 很 大 , 可 以 开 一 个 二 维 数 组 做 d p 然后观察这个n不是很大,可以开一个二维数组做dp ndp
这 道 题 只 能 符 合 区 间 d p 这道题只能符合区间dp dp
由 于 张 老 师 能 跑 到 两 端 就 能 跑 完 全 程 , 所 以 说 明 最 后 肯 定 在 两 端 的 某 一 个 点 由于张老师能跑到两端就能跑完全程,所以说明最后肯定在两端的某一个点
然 后 就 可 以 列 出 一 个 d p 模 型 然后就可以列出一个dp模型 dp
d p [ i ] [ j ] [ 0 / 1 ] 在 i 到 j 这 个 区 间 里 , 张 老 师 在 i ( 0 ) 或 者 j ( 1 ) 花 费 的 最 小 时 间 dp[i][j][0/1]在i到j这个区间里,张老师在i(0)或者j(1)花费的最小时间 dp[i][j][0/1]iji(0)j(1)
然 后 这 个 区 间 必 须 要 包 括 起 点 x , 在 这 个 基 础 上 列 区 间 d p 即 可 然后这个区间必须要包括起点x,在这个基础上列区间dp即可 xdp
然 后 找 状 态 转 移 方 程 然后找状态转移方程
可 能 的 状 态 是 从 最 右 边 走 到 最 左 边 , 或 者 最 左 边 走 到 最 右 边 可能的状态是从最右边走到最左边,或者最左边走到最右边
也 可 能 最 左 边 往 左 移 一 个 点 , 或 者 最 右 边 往 右 移 一 个 点 也可能最左边往左移一个点,或者最右边往右移一个点
每 次 移 动 只 要 保 证 移 动 到 的 那 个 点 的 t i 要 大 于 等 于 到 的 时 间 每次移动只要保证移动到的那个点的t_i要大于等于到的时间 ti
这 样 就 可 以 列 状 态 转 移 方 程 了 这样就可以列状态转移方程了

d p [ l ] [ r ] [ 0 ] = m i n ( d p [ l ] [ r ] [ 0 ] , d p [ l + 1 ] [ r ] [ 0 ] + a [ l + 1 ] − a [ l ] ) ; dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][0]+a[l+1]-a[l]); dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][0]+a[l+1]a[l]);
d p [ l ] [ r ] [ 0 ] = m i n ( d p [ l ] [ r ] [ 0 ] , d p [ l + 1 ] [ r ] [ 1 ] + a [ r ] − a [ l ] ) ; dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][1]+a[r]-a[l]); dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][1]+a[r]a[l]);
d p [ l ] [ r ] [ 1 ] = m i n ( d p [ l ] [ r ] [ 1 ] , d p [ l ] [ r − 1 ] [ 1 ] + a [ r ] − a [ r − 1 ] ) ; dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][1]+a[r]-a[r-1]); dp[l][r][1]=min(dp[l][r][1],dp[l][r1][1]+a[r]a[r1]);
d p [ l ] [ r ] [ 1 ] = m i n ( d p [ l ] [ r ] [ 1 ] , d p [ l ] [ r − 1 ] [ 0 ] + a [ r ] − a [ l ] ) ; dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][0]+a[r]-a[l]); dp[l][r][1]=min(dp[l][r][1],dp[l][r1][0]+a[r]a[l]);

转 移 的 前 提 就 是 上 面 说 的 要 保 证 下 一 个 到 的 点 , 大 于 等 于 到 达 的 时 间 转移的前提就是上面说的要保证下一个到的点,大于等于到达的时间
最 后 的 结 果 是 d p [ l ] [ r ] [ 0 ] 和 d p [ l ] [ r ] [ 1 ] 取 最 小 值 最后的结果是dp[l][r][0]和dp[l][r][1]取最小值 dp[l][r][0]dp[l][r][1]
如 果 两 个 都 为 i n f , 那 么 说 明 不 存 在 如果两个都为inf,那么说明不存在 inf
AC代码

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int mod=1e9+7;
//const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};

int dp[1010][1010][2],a[1010],t[1010];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    int pos;
    for(int i=1;i<=n;i++){
        cin>>t[i];
        if(t[i]==0)pos=i;
    }
    memset(dp,inf,sizeof dp);
    dp[pos][pos][0]=dp[pos][pos][1]=0;
    for(int len=1;len<=n;len++)
        for(int l=max(1,pos-len+1),r;l<=pos&&l+len-1<=n;l++){
            r=l+len-1;
            if(dp[l+1][r][0]+a[l+1]-a[l]<=t[l])
                dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][0]+a[l+1]-a[l]);
            if(dp[l+1][r][1]+a[r]-a[l]<=t[l])
                dp[l][r][0]=min(dp[l][r][0],dp[l+1][r][1]+a[r]-a[l]);
            if(dp[l][r-1][1]+a[r]-a[r-1]<=t[r])
                dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][1]+a[r]-a[r-1]);
            if(dp[l][r-1][0]+a[r]-a[l]<=t[r])
                dp[l][r][1]=min(dp[l][r][1],dp[l][r-1][0]+a[r]-a[l]);
        }
    int ans=min(dp[1][n][0],dp[1][n][1]);
    if(ans==inf)cout<<-1;
    else cout<<ans;
    return 0;
}


J.斐波那契和

题意:
给 定 n 和 k 求 如 下 式 子 给定n和k求如下式子 nk
∑ i = 1 n i k F i b ( i ) \sum_{i=1}^n {i^kFib(i)} \quad i=1nikFib(i)
F i b ( i ) 表 示 第 i 位 斐 波 那 契 数 Fib(i)表示第i位斐波那契数 Fib(i)i
题解:
n < = 1 e 18 n<=1e18 n<=1e18
B e r l e k a m p − M a s s e y 算 法 Berlekamp-Massey算法 BerlekampMassey
这 道 题 很 玄 学 的 用 了 一 发 B M 算 法 这道题很玄学的用了一发BM算法 BM
大 概 意 思 就 是 给 出 前 几 位 , 然 后 用 算 法 能 跑 出 第 n 位 的 数 大概意思就是给出前几位,然后用算法能跑出第n位的数 n
所 以 我 就 用 暴 力 算 出 了 前 1000 位 数 直 接 跑 B M 算 法 所以我就用暴力算出了前1000位数直接跑BM算法 1000BM
代 码 其 实 我 也 不 太 懂 , 直 接 抄 的 模 版 代码其实我也不太懂,直接抄的模版
模版来自:https://www.cnblogs.com/zzqsblog/p/6877339.html
直 接 套 模 版 的 题 , 直 接 上 代 码 直接套模版的题,直接上代码
A C 代 码 AC代码 AC

/*
    Author:zzugzx
    Lang:C++
    Blog:blog.csdn.net/qq_43756519
*/
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define endl '\n'
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
//const int mod=1e9+7;
const int mod=998244353;
const double eps = 1e-10;
const double pi=acos(-1.0);
const int maxn=1e6+10;
const ll inf=0x3f3f3f3f;
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};


const int MOD=998244353;
const int SZ=maxn;
ll qp(ll a,ll b)
{
    ll x=1; a%=MOD;
    while(b)
    {
        if(b&1) x=x*a%MOD;
        a=a*a%MOD; b>>=1;
    }
    return x;
}
namespace linear_seq {
inline vector<ll> BM(vector<ll> x)
{
    vector<ll> ls,cur;
    int pn=0,lf,ld;
    for(int i=0;i<int(x.size());++i)
    {
        ll t=-x[i]%MOD;
        for(int j=0;j<int(cur.size());++j)
            t=(t+x[i-j-1]*(ll)cur[j])%MOD;
        if(!t) continue;
        if(!cur.size())
        {cur.resize(i+1); lf=i; ld=t; continue;}
        ll k=-t*qp(ld,MOD-2)%MOD;
        vector<ll> c(i-lf-1); c.pb(-k);
        for(int j=0;j<int(ls.size());++j) c.pb(ls[j]*k%MOD);
        if(c.size()<cur.size()) c.resize(cur.size());
        for(int j=0;j<int(cur.size());++j)
            c[j]=(c[j]+cur[j])%MOD;
        if(i-lf+(int)ls.size()>=(int)cur.size())
            ls=cur,lf=i,ld=t;
        cur=c;
    }
    vector<ll>&o=cur;
    for(int i=0;i<int(o.size());++i)
        o[i]=(o[i]%MOD+MOD)%MOD;
    return o;
}
int N; ll a[SZ],h[SZ],t_[SZ],s[SZ],t[SZ];
inline void mull(ll*p,ll*q)
{
    for(int i=0;i<N+N;++i) t_[i]=0;
    for(int i=0;i<N;++i) if(p[i])
        for(int j=0;j<N;++j)
            t_[i+j]=(t_[i+j]+p[i]*q[j])%MOD;
    for(int i=N+N-1;i>=N;--i) if(t_[i])
        for(int j=N-1;~j;--j)
            t_[i-j-1]=(t_[i-j-1]+t_[i]*h[j])%MOD;
    for(int i=0;i<N;++i) p[i]=t_[i];
}
inline ll calc(ll K)
{
    for(int i=N;~i;--i) s[i]=t[i]=0;
    s[0]=1; if(N!=1) t[1]=1; else t[0]=h[0];
    for(;K;mull(t,t),K>>=1) if(K&1) mull(s,t); ll su=0;
    for(int i=0;i<N;++i) su=(su+s[i]*a[i])%MOD;
    return (su%MOD+MOD)%MOD;
}
inline int gao(vector<ll> x,ll n)
{
    if(n<int(x.size())) return x[n];
    vector<ll> v=BM(x); N=v.size(); if(!N) return 0;
    for(int i=0;i<N;++i) h[i]=v[i],a[i]=x[i];
    return calc(n);
}
}
ll fff[3000]={1,1,1};
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ll n,k;
    cin>>n>>k;
    vector<ll> qqq;
    qqq.pb(0);
    for(int i=3;i<2000;i++)
        fff[i]=(fff[i-1]+fff[i-2])%MOD;
    for(int i=1;i<=1000;i++){
        ll sss=0;
        for(int j=1;j<=i;j++)
            sss=(sss+qp(j,k)*fff[j])%MOD;
        qqq.pb(sss);
    }
    cout<<linear_seq::gao(qqq,n);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值