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

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

链接:https://ac.nowcoder.com/acm/contest/5477/A
来源:牛客网

天才程序员菜哭武和张老师有一天到一个城市旅游,旅途中菜哭武觉得无聊就想和张老师玩一个游戏。菜哭武有n个石子,每个石子都标有1到n之间到数,且各不相同,一开始他们会随机从这堆石子选一个石子放置到一个集合中,张老师选的数是a,菜哭武选的是b(a和b不相同)。接下来菜哭武和张老师轮流按照如下规则拿走一个石子:当石子x能被拿走时,当且仅当集合存在y和z,满足x等于y+z或者y-z,当x被拿走时,把它放到集合中。谁完成最后一轮操作时,谁获胜。张老师总是先手,于是张老师就好奇当决定好a和b时,他是否总是能获胜,你能帮助一下张老师吗?

输入描述:

第一行一个整数T(1≤T≤500),表示共有T组测试数据。
对于每组测试数据,第一行三个整数n(2≤n≤20000)、a和b(1≤a,b≤n, a≠b)。

输出描述:

若张老师能获胜输出Yes,反之No。

示例1

输入

复制
16
2 1 2
3 1 3
67 1 2
100 1 2
8 6 8
9 6 8
10 6 8
11 6 8
12 6 8
13 6 8
14 6 8
15 6 8
16 6 8
1314 6 8
1994 1 13
1994 7 12

输出

复制
No
Yes
Yes
No
No
No
Yes
Yes
No
No
Yes
Yes
No
Yes
No
No
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main(){
    int t;
    cin>>t;
    for(int i=0;i<t;i++){
        int n,a,b;
        cin>>n>>a>>b;
        if((n/__gcd(a,b))%2)cout<<"Yes\n";
        else cout<<"No\n";
    }
    return 0;
}

B。伤害计算

链接:https://ac.nowcoder.com/acm/contest/5477/B
来源:牛客网

勇士菜哭武获得了一把新的武器,武器有特殊的伤害计算方式。武器的伤害计算方式由若干个部分的和组成,用+号连接。每一部分可以是一个整数a,或者是一个公式ndx。其中a表示固定伤害a点;ndx表示掷n个x面骰子,伤害是所有骰子点数的和。总伤害是每一部分伤害的和。

比如2d6+1d70+3,表示掷两个6面骰子和一个70面骰子(不一定实际存在70面骰子,可以理解成1到70当中随机选择一个整数),再加上固定伤害3点。

他正准备挑选一把好武器,需要计算新武器的伤害期望值,想让你帮他计算一下。

输入描述:

输入一个字符串,表示伤害计算公式。字符串长度不超过5000,对于每一个部分,1≤a, n, x≤1000。a,n,x都是整数。 

输出描述:

输出一个数,表示伤害的期望值。如果不是整数,小数点后位数保留最少,即最终结果只有可能是整数或者小数点后是.5的形式,如果不是整数,那么保留一位小数。

示例1

输入

复制
1d6+1d70+1d10+6

输出

复制
50.5
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
char str[10010];
int parse(char *p){
    char *d = strchr(p,'d');
    if(d==NULL){
        return atoi(p)*2;
    }else{
        *d='\0';
        int n = atoi(p);
        int x = atoi(d+1);
        return (x+1)*n;
    }
}
 
int main()
{
    cin>>str;
    int len = strlen(str);
    char *p = str;
    ll ans = 0;
    while(1){
        char *ne = strchr(p,'+');
        if(ne==NULL){
            ans += parse(p);
            break;
        }else{
            *ne='\0';
            ans+=parse(p);
            p=ne+1;
        }
 
    }
    cout<<ans/2;
    if(ans%2)cout<<".5";
    cout<<endl;
    return 0;
}

C.张老师的旅行

链接:https://ac.nowcoder.com/acm/contest/5477/C
来源:牛客网

张老师到了一个王国去旅游,王国有n个景点,张老师到达这个城市所在的车站恰好位于第x个景点,这个王国非常特别,恰好所有著名的景点都在分布在直线上,每个景点在坐标pi上(单位:公里),张老师身体非常好,每走一公里花费一分钟。每个景点都有一个打卡点,并且必须在不迟于相应的时间(时间从张老师到达王国开始计算)前到达才能打卡成功并且给以一个打卡标记,集齐所这些标记就能获得一个大礼包。由于张老师非常想要大礼包,并且因为张老师还着急去下一个王国旅游,所以张老师希望用的时间尽量少,你能帮帮张老师吗?

输入描述:

输入的第一行,包含一个整数n(1≤n≤1000)。
第二行包含n个整数pi(1≤pi≤100000),第i个整数pi为第i个景点的坐标(坐标从小到大排列)。
最后一行包含n个整数ti(0≤ti≤10,000,000),ti表示第i个景点最迟到达的时间,时间为0则表示张老师所在车站的位置且只有一个为0。

输出描述:

输出所需的最少时间,若无解输出-1。
示例1

输入

复制
4
1 2 3 4
0 1 2 3

输出

复制
3
#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
int dp[1001][1001][2];
struct node{
    int t=0,l=0;
}a[1001],b[1001],c[1001];
bool cmp(node x,node y){return x.l<y.l;}
int main(){
     int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i].l;
    for(int i=1;i<=n;i++)cin>>a[i].t;
    sort(a+1,a+1+n,cmp);
    int k;
    for(int i=1;i<=n;i++){
        if(a[i].t==0)k=i;
    }
    memset(dp,INF,sizeof(dp));
    int x=0,y=0;
    dp[0][0][1]=dp[0][0][0]=0;
    for(int i=k-1;i>0;i--)x++,b[x].l=abs(a[i].l-a[k].l),b[x].t=a[i].t;//求左边点集
    for(int i=k+1;i<=n;i++)y++,c[y].l=abs(a[i].l-a[k].l),c[y].t=a[i].t;//求右边点集
    for(int i=0;i<=x;i++){
        for(int j=0;j<=y;j++){
            if(i)dp[i][j][0]=min(dp[i-1][j][0]+b[i].l-b[i-1].l,dp[i-1][j][1]+c[j].l+b[i].l);
            if(j)dp[i][j][1]=min(dp[i][j-1][1]+c[j].l-c[j-1].l,dp[i][j-1][0]+c[j].l+b[i].l);
            if(dp[i][j][0]>b[i].t&&dp[i][j][1]>c[j].t){cout<<-1;return 0;}
            if(dp[i][j][0]>b[i].t)dp[i][j][0]=INF;
            if(dp[i][j][1]>c[j].t)dp[i][j][1]=INF;
        }
    }
    cout<<min(dp[x][y][0],dp[x][y][1]);
}

D。车辆调度

链接:https://ac.nowcoder.com/acm/contest/5477/D
来源:牛客网

张老师设计了一个智能调度系统来控制他的遥控车队,今天,他带着他的车队来到黄渡理工大学的一块空地上测试这个系统。
这块空地可以描述为一个 w * h 大小的长方形,广场上有一些障碍物,几个目标点,当然,还有张老师的车队。
每分钟,调度系统会智能地向其中的一辆遥控车发送以下指令的其中一条:

  1. 向北走,直到撞到空地的边界、障碍物或其他遥控车;

  2. 向南走,直到撞到空地的边界、障碍物或其他遥控车;

  3. 向西走,直到撞到空地的边界、障碍物或其他遥控车;

  4. 向东走,直到撞到空地的边界、障碍物或其他遥控车;

    每条指令都会在一分钟之内完成,也就是说,空地上最多只有一辆遥控车在运动。此外,当遥控车无法向相应的方向移动时,它会停在原地。

你想知道,在第 k 分钟时,有没有可能有任意一辆遥控车处在任意一个目标点上。

输入描述:

第一行输入三个数字w, h, k (1 ≤  w, h ≤ 10, 1 ≤ k ≤ 5) ,含义在题目描述中已给出。

接下来h行,每行输入一个长度为w的字符串si,其中第i行的第j个字符表示(i, j)位置的状态。

其中,'R'代表该位置初始有一辆遥控车,'X'代表该位置有障碍物,'D'代表该位置是一个目标点,'.'代表该位置可以正常通过。

数据保证广场上的遥控车不超过4辆。

输出描述:

如果k分钟后有可能有任意一个遥控车处在任意一个目标点上,输出YES,否则输出NO。

示例1

输入

复制
6 5 4
.....R
...X..
..D...
....D.
R.R...

输出

复制
YES

说明

样例中,遥控车可以按下述路线移动:

.....R

...X..

..D...

....D.

R.R...
 

R.....

...X..

..D...

....D.

R.R...
 

R.....

R..X..

..D...

....D.

..R...
 

R.....

..RX..

..D...

....D.

..R...
 

R.....

..RX..

..R...

....D.

......

4分钟时,有一辆遥控车达到了目标点。于是输出YES

#define debug
#ifdef debug
#include <time.h>
#include "/home/majiao/mb.h"
#endif
 
 
#include <iostream>
#include <algorithm>
#include <vector>
#include <string.h>
#include <map>
#include <set>
#include <stack>
#include <queue>
#define MAXN ((int)1e5+7)
#define ll long long int
#define QAQ (0)
#include <sstream>
 
using namespace std;
 
int n, m, K;
int dr[] = { 1, -1, 0, 0 }; //4个方向
int dc[] = { 0, 0, 1, -1 };
char mtx[16][16], tmp[16][16]/*用来模拟车的位置(0没有车,1有车)*/;
struct Node {
    int r, c;
} ;
 
bool check(int x, int y) { //判断是否越界
    return mtx[x][y];
}
 
bool ok = false;
void dfs(int level, vector<Node> v) {
    if(ok) return ;
    if(level == K) { //第level步恰好有在目标格子的车就ok=true
        for(int i=1; i<=n; i++)
            for(int k=1; k<=m; k++)
                if(tmp[i][k] && mtx[i][k]=='D') {
                    ok = true;
                    goto p;
                }
        p:
        return ;
    }
    for(int i=0; i<int(v.size()) && !ok; i++) { //枚举每辆车
        Node& no = v[i];
        //每辆车都有4个方向
        for(int k=0; k<4 && !ok; k++) {
            int tmpr = no.r, tmpc = no.c;
 
            //移动到这个方向,直到撞墙或撞车
            while(check(no.r+dr[k], no.c+dc[k]) &&
                    (mtx[no.r+dr[k]][no.c+dc[k]]!='X')
                    && !(tmp[no.r+dr[k]][no.c+dc[k]])) {
                no.r += dr[k], no.c += dc[k];
            }
            tmp[no.r][no.c] = true;
            tmp[tmpr][tmpc] = false;
            dfs(level+1, v); //递归下一步
            tmp[no.r][no.c] = false; //还原现场
            tmp[tmpr][tmpc] = true;
            no.r = tmpr, no.c = tmpc;
        }
    }
}
 
int main() {
#ifdef debug
    freopen("test", "r", stdin);
    clock_t stime = clock();
#endif
    scanf("%d %d %d ", &m, &n, &K);
    vector<Node> vec;
    for(int i=1; i<=n; i++) {
        scanf("%s ", mtx[i]+1);
        for(int k=1; k<=m; k++)
            if(mtx[i][k] == 'R') vec.push_back({i, k});
    }
    dfs(0, vec);
    printf("%s\n", ok ? "YES" : "NO");
 
 
 
#ifdef debug
    clock_t etime = clock();
    printf("rum time: %lf 秒\n",(double) (etime-stime)/CLOCKS_PER_SEC);
#endif
    return 0;
}

E 弦

链接:https://ac.nowcoder.com/acm/contest/5477/E
来源:牛客网

给定一个圆,圆上有2N个互不重叠的点。每次操作随机选择两个先前未选择过的点连一条弦,共连成N条弦,求所有弦不交的概率。

输入描述:

一行,只有一个整数N(1≤N≤107)。

输出描述:

一行,即答案。答案应以模109+7的形式输出。正式的说,令M=109+7,答案可以表示为最简分数p/q的形式,其中p和q为整数且q与M互质。输出整数x满足 0≤x<M且 x⋅q ≡ p(mod M)。

示例1

输入

复制
2

输出

复制
666666672
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
ll qkpow(ll a, ll b) {
    ll ans = 1;
    while (b) {
        if (b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
ll getInv(ll a) { return qkpow(a, mod - 2); }  //求一个数的逆元
int main() {
    ll n;
    cin >> n;
    ll ans = 1;
    for (int i = 2; i <= n + 1; ++i) ans *= i, ans %= mod;
    ans = qkpow(2, n) * getInv(ans) % mod;
    cout << ans << endl;
    return 0;
}

F排列计算

链接:https://ac.nowcoder.com/acm/contest/5477/F
来源:牛客网

天才程序员菜哭武和石头组队参加一个叫做国际排列计算竞赛 (International Competition of Permutation Calculation, ICPC) 的比赛,这个比赛的规则是这样的: 

一个选手给出一个长度为 n 的排列,另一个选手给出 m 个询问,每次询问是一个形如 (l, r) 的数对,查询队友给出的排列中第 l 个数到第 r 个数的和,并将查询到的这个区间和加入总分,最后总分最高的队伍就能获胜。

石头手速很快,在比赛一开始就给出了 m 个询问;菜哭武也很强,他总是能找到最合适的排列,使得他们队的总分尽可能高。

在看比赛直播的你看到了石头给出的 m 个询问,聪明的你能不能预测出他们队伍最终的得分呢?

一个排列是一个长度为 n 的数列,其中 1 ~ n 中的每个数都在数列中恰好出现一次。比如 [1, 3, 2] 是一个排列,而 [2, 1, 4] 和 [1, 2, 3, 3] 不是排列。 

输入描述:

第一行输入两个数 n (1≤n≤2×105) 和 m (1≤m≤2×105) 。
接下来 m 行,每行输入两个数 l 和 r ,代表这次查询排列中第 l 个到第 r 个的和。

输出描述:

输出一个整数,代表他们队伍总分的最大值。
示例1

输入

复制
7 3
1 3
3 7
5 6

输出

复制
46

说明

一个符合条件的排列是 [1,3, 6, 4, 7, 5, 2],于是最终的得分为 (1 + 3 + 6) + (6 + 4 + 7 + 5 + 2) + (7 + 5) = 46

#include<iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int d[N];
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++){
        int l, r;
        scanf("%d%d", &l, &r);
        d[l] ++ ,d[r + 1]--;
    }
    for(int i = 1; i <= n; i++){
        d[i] += d[i - 1];
    }
    sort(d + 1, d + n + 1);
    ll ans = 0;
    for(int i = 1; i <= n; i++)
        ans += 1ll * d[i] * i;
    printf("%lld\n", ans);
    return 0;
}

G 硬币游戏

链接:https://ac.nowcoder.com/acm/contest/5477/G
来源:牛客网

张老师和石头每年校赛都要决定谁来出最后一道题,都是通过玩游戏,每年菜哭武都能发现新的游戏。今年菜哭武选择了翻硬币游戏,谁能赢就不用出最后一道题。

有n个硬币从左到右排开,有一些是正面,有一些是反面。张老师和石头轮流选择连续最多k个(至少一个)硬币翻面,其中最右边一个硬币一定是从正面翻成反面,谁先不能行动就输掉游戏。张老师首先选择。

张老师最近忙于毕设没时间出题,也不想花时间玩游戏,想让你帮他研究一下他能否获胜。

输入描述:

第一行两个正整数n, k(1≤k≤n≤106),表示总硬币个数和同时连续最多翻转的硬币个数。

第二行一个长度是n的01字符串s,第i个字符表示从左到右第i个硬币的正反面情况,1表示正面,0表示反面。

输出描述:

输出一行,如果张老师能赢,则输出Yes,否则输出No。

示例1

输入

复制
10 2
1001001001

输出

复制
No
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char str[1001000];
int n;
int k;
int maxsg;
int sg(int i){
    int ans = i & (-i);
    return min(ans,maxsg);
}
int main()
{
 
    cin>>n>>k;
    cin>>str;
    maxsg=1;
    while(k>1){
        k/=2;
        maxsg*=2;
    }
    int sgsum = 0;
    for(int i=0;i<n;i++){
        if(str[i]=='1')
            sgsum ^= sg(i+1);
    }
    if(sgsum)cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
    return 0;
}

H 时空栈

链接:https://ac.nowcoder.com/acm/contest/5477/H
来源:牛客网

最近,dzerzhinski刚刚学会了栈这个基础数据结构,但是他却被一道简单题难住了。

栈是一种特殊的线性表,它只能在固定的一段进行插入和删除操作。栈满足“后进先出”的性质,也就是每次出栈的元素一定是最后入栈的元素。 这道题要求实现一个栈,模拟元素的进栈和出栈和查看栈顶元素。

但是dzerzhinski的栈有时空能力,每一个操作都会首先跳转到某个时间点,然后执行这个操作,再回到当前的时间。最终状态是所有操作按照时间顺序依次执行的结果。

意识到问题的dzerzhinski发现这是一个很好的训练项目,决定让你来实现一个这样有时空能力的栈。

输入描述:

第一行一个整数n(1≤n≤2×105),表示有n个操作。

后面n行,每行表示一个操作。第一个数op表示操作类型。

op=0:入栈操作。输入三个数op,t, v,表示回到时间点t,然后整数v入栈。

op=1:出栈操作。输入两个数op,t,表示回到时间t,栈顶元素出栈。

op=2:查询栈顶元素。输入两个数op,t,表示查询时间t时的栈顶元素。

其中所有操作的时间点t满足1≤t≤109,且都不相等,保证能够有唯一的操作顺序。所有入栈整数v满足1≤v≤109。保证任意时刻不会出现空栈时出栈操作,或者在空栈的时刻查询栈顶元素操作。

输出描述:

对于每一个查询操作,输出一行,表示到当前为止的所有操作栈顶元素。

示例1

输入

复制
8
0 2 2
2 6
2 4
0 1 1
2 7
1 5
2 3
2 8

输出

复制
2
2
2
2
1

说明

部分解释:

查询7时刻栈顶的时候,之前在2时刻入栈2,在1时刻入栈1。按照时间顺序2在1之后入栈,所以结果是2;

查询3时刻的时候,所有操作是2时刻入栈2,1时刻入栈1,5时刻出栈,但是5时刻的操作不会在3时刻之前产生,所以结果还是2;

查询8时刻的时候,所有操作是2时刻入栈2,1时刻入栈1,5时刻出栈。按照时间顺序是1入栈,2入栈,2出栈,所以栈顶是1。

#include<iostream>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;
 
#define lson rt * 2
#define rson rt * 2 + 1
 
const int N = 2e5 + 100;
 
struct node {
    int sum, ma;
    node() {
        sum = ma = 0;
    }
    void set(int x) {
        sum = ma = x;
    }
}sa[N * 4];
 
node cal(node a, node b) {
    node c;
    c.sum = a.sum + b.sum;
    c.ma = max(a.ma + b.sum, b.ma);
    return c;
}
 
int n, tp;
int op[N], aa[N], bb[N], has[N], rev[N];
 
void insert(int id, int l, int r, int rt) {
    if (l == r) {
        int i = rev[l];
        if (op[i] == 0) sa[rt].set(1);
        else sa[rt].set(-1);
        return;
    }
    int m = (l + r) / 2;
    if (id <= m) insert(id, l, m, lson);
    else insert(id, m + 1, r, rson);
    sa[rt] = cal(sa[lson], sa[rson]);
}
 
int res;
 
void query(node x, int l, int r, int rt) {
    if (l == r) {
        res = bb[rev[l]];
        return;
    }
    int m = (l + r) / 2;
    if (cal(sa[rson], x).ma > 0) query(x, m + 1, r, rson);
    else query(cal(sa[rson], x), l, m, lson);
}
 
void query(int id, node &x, int l, int r, int rt) {
    if (l == r) {
        x = sa[rt];
        if (sa[rt].sum > 0) query(x, l, r, rt);
        return;
    }
    int m = (l + r) / 2;
    if (id <= m) {
        query(id, x, l, m, lson);
    }
    else {
        query(id, x, m + 1, r, rson);
        if (!res) {
            if (cal(sa[lson], x).ma > 0) {
                query(x, l, m, lson);
            }
            x = cal(sa[lson], x);
        }
    }
}
 
int main() {
    //freopen("0.txt", "r", stdin);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", op + i);
        if (op[i]) scanf("%d", aa + i);
        else scanf("%d%d", aa + i, bb + i);
        has[++tp] = i;
    }
    sort(has + 1, has + 1 + tp);
    tp = unique(has + 1, has + 1 + tp) - has;
    for (int i = 1; i <= n; i++) {
        aa[i] = lower_bound(has + 1, has + tp, aa[i]) - has;
        if (op[i] != 2) rev[aa[i]] = i;
    }
    for (int i = 1; i <= n; i++) {
        if (op[i] == 2) {
            res = 0;
            node x;
            query(aa[i], x, 1, tp, 1);
            printf("%d\n", res);
        }
        else insert(aa[i], 1, tp, 1);
    }
    return 0;
}

I纸牌

链接:https://ac.nowcoder.com/acm/contest/5477/I
来源:牛客网

桌上有一叠共N张牌,从顶向下标号为1~N。张老师打算对这一叠牌做k次操作,其中第i次操作会将牌堆顶的牌放在牌堆中的某个位置,从而满足这张牌成为自顶向下第(i - 1) % (N - 1) + 2张牌,其中%表示取模操作。张老师想知道k次操作以后这叠牌自顶向下的编号情况,你能告诉他吗?

输入描述:

一行,两个整数N和k(2≤N≤106, 0≤k≤1018)。

输出描述:

一行共N个数,第i个数为操作k次后自顶向下第i张牌的编号。数字间用空格间隔。

示例1

输入

复制
5 3

输出

复制
3 2 4 1 5

说明

以样例为例
第1次操作后的结果:2 1 3 4 5
第2次操作后的结果:1 3 2 4 5
第3次操作后的结果:3 2 4 1 5

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 10;
typedef long long ll;
int n;
ll k;
int p[maxn], tmp[maxn];
bool vis[maxn];
void getNext(int * a, int k)
{
    memset(tmp, 0, sizeof tmp);
    for(int i = 1; i <= n; ++i) tmp[(i << 1) - 1] = a[i];
    int cur = 1;
    for(int i = 1; i <= k; ++i)
    {
        while(!tmp[cur]) ++cur;
        swap(tmp[cur], tmp[(i + 1) << 1]);
    }
    int tot = 0;
    for(int i = 1; i <= 2 * n; ++i)
        if(tmp[i])
            a[++tot] = tmp[i];
}
void getCycle(int * a, ll k)
{
    for(int i = 1; i <= n; ++i)
        if(!vis[i])
        {
            int cur = i, tot = 0;
            while(!vis[cur])
            {
                tmp[++tot] = cur;
                vis[cur] = 1;
                cur = a[cur];
            }
            tmp[0] = tmp[tot];
            int shift = k % tot;
            for(int j = 0; j < tot; ++j)
                a[tmp[j]] = tmp[(j + shift) % tot];
        }  
}
int main()
{
    cin >> n >> k;
    for(int i = 1; i <= n; ++i) p[i] = i;
    getNext(p, n - 1);
    getCycle(p, k / (n - 1));
    getNext(p, k % (n - 1));
    for(int i = 1; i <= n; ++i) cout << p[i] << ' ';
    cout << '\n';
    return 0;
}
Dzerzhinski: k – k % (n – 1)部分也可以快速幂计算。整体复杂度是O(n log(k/n))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int arr[1001000];
int temp[2002000];
int powans[1001000];
int n;
ll k;
void step(int pre[],int step){
    for(int i=0;i<n;i++){
        temp[i*2]=pre[i];
        temp[i*2+1]=0;
    }
    int now = 0;
    for(int i=0;i<step;i++){
        while(temp[now]==0)now++;
        swap(temp[now],temp[(i+1)*2+1]);
    }
    now=0;
    for(int i=0;i<2*n;i++){
        if(temp[i])pre[now++]=temp[i];
    }
}
void mul(int a[],int b[]){
    for(int i=0;i<n;i++){
        temp[i]=a[b[i]-1];
    }
    for(int i=0;i<n;i++)a[i]=temp[i];
 
}
 
void powmod(int a[],ll p){
    for(int i=0;i<n;i++)powans[i]=i+1;
    while(p){
        if(p%2){
            mul(powans,a);
        }
        p/=2;
        mul(a,a);
    }
}
 
int main(){
    cin>>n>>k;
    for(int i=0;i<n;i++)arr[i]=i+1;
    step(arr,n-1);
    powmod(arr,k/(n-1));
    step(powans,k%(n-1));
 
    for(int i=0;i<n;i++){
         cout<<powans[i]<<" ";
    }
    cout<<endl;
    return 0;
}

J 斐波那契和

链接:https://ac.nowcoder.com/acm/contest/5477/J
来源:牛客网

Fib(i)表示斐波那契函数,Fib(n)=Fib(n-1)+Fib(n-2),如Fib(1)=1,Fib(2)=1,Fib(3)=2,Fib(4)=3,Fib(5)=5,Fib(6)=8。

给定正整数n和k,求:
∑i=1nikFib(i)\sum_{i=1}^{n} i^kFib(i)i=1nikFib(i)

由于结果太大,你需要把求和的结果对998,244,353取余。

输入描述:

输入一行,包含两个整数n和k(1≤n≤10

18

,1≤k≤100)

输出描述:

输出一个整数,表示求和对998,244,353取余的结果。

示例1

输入

复制
5 2

输出

复制
196

说明

样例解释:

1*1*Fib(1) + 2*2*Fib(2) + 3*3*Fib(3) + 4*4*Fib(4) + 5*5*Fib(5) = 196

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll modn = 998244353;
struct Mat{
    ll a[105][105];
    Mat(){
        memset(a,0,sizeof(a));\
    }
    ll* operator[](int i){
        return a[i];
    }
};
int W=104;
Mat operator *(Mat &a, Mat &b){
    Mat ans;
    for(int i=0;i<W;i++)for(int j=0;j<W;j++){
        for(int k=0;k<W;k++) {
            ans[i][j] += a[i][k] * b[k][j] % modn;
        }
        ans[i][j]%=modn;
    }
    return ans;
}
ll C[505][505];
ll F_b[505];
ll F[505];
ll pown[505];
Mat powmod(Mat a, ll p){
    Mat ans;
    for(int i=0;i<W;i++)ans[i][i]=1;
    while(p) {
        if (p % 2) {
            ans = ans * a;
        }
        p /= 2;
        a = a * a;
    }
    return ans;
}
 
int main()
{
    C[0][0]=1;
    for(int i=0;i<500;i++){
        for(int j=0;j<=i;j++){
            C[i+1][j]+=C[i][j];
            C[i+1][j+1]+=C[i][j];
            C[i+1][j]%=modn;
            C[i+1][j+1]%=modn;
        }
    }
    ll n,k;
    cin>>n>>k;
    pown[0]=1;
    for(int i=1;i<=k;i++)
        pown[i]=pown[i-1]*(n%modn)%modn;
    W=k+3;
    Mat ans;
    ans[0][0]=1;
    ans[0][1]=1;
    ans[1][0]=1;
    ans[2][2]=1;
    ans[2][0]=1;
    ans[2][1]=1;
    for(int i=1;i<=k;i++){
        for(int j=0;j<=i;j++){
            ans[i+2][j+2]=C[i][j];
        }
    }
 
    ans = powmod(ans,n);
 
    for(int i=0;i<=k;i++)
        F_b[i]=ans[i+2][1];
 
    F[0]=F_b[0];
    for(int i=1;i<=k;i++){
        ll fi = F_b[i];
        ll flag = 1;
        for(int j=0;j<i;j++){
            flag = modn-flag;
            fi += flag * C[i][j] %modn *pown[i-j]%modn * F[j]%modn;
        }
        fi %= modn;
        fi *= flag;
        F[i]=fi%modn;
    }
    cout<<F[k]<<endl;
 
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/mxw000120/p/12901992.html