SDUT 2021 Spring Individual Contest(for 20) - 18(补题)

D - Counting Paths

A binary tree is a tree data structure in which each node has at most two children, which are referred to as the left child and the right child.

Consider an infinite binary tree with each node has exactly two children, and two given integers a and b, count the number of paths in the infinite binary tree that satisfy the following rules:

The path starts from the root of the tree.
The length of the path is equal to a (The length of a path is the total number of edges from the root to the final node on that path).
The number of change of direction along the path is equal to b. Change of direction means, going to your right child if you are the left child of your parent or vise versa.
As the number of paths can be too large, print it (modulo 109 + 7).

Input
The first line of the input contains an integer T (1 ≤ T ≤ 105), where T is the number of the test cases.

Each test case has one line that contains two integers a and b (0 ≤ b < a ≤ 105), the length of the path and the number of change of direction along it.

Output
For each test case, print a single integer that represents the number of paths that satisfy the mentioned rules (modulo 109 + 7).

Example
Input

2
2 1
4 3
Output
2
2
题意: 一颗满二叉树(往下可以无限延伸),给你路径长度a,改变方向次数b,问你满足这个条件的路径数有几条。答案要取模
思路: 就是组合数的思想,路径长度为a,那路上就会有a-1个拐点,一共要转向b次,也就是C(a-1,b),最后得数再✖2,期间求组合数要用到逆元,因为涉及到取模运算。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5 + 10, mod = 1e9 + 7;
int fact[N], infact[N];
int qmi(int q, int k, int p) {
    int res = 1;
    while (k) {
        if (k & 1) res = (ll)res * q % p;
        q = (ll)q * q % p;
        k >>= 1;
    }
    return res;
}
void init() {
    fact[0] = infact[0] = 1;
    for (int i = 1; i < N; i++) {
        fact[i] = (ll)fact[i - 1] * i % mod;
        infact[i] = (ll)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
    }
}

int main() {
    int t;
    scanf("%d", &t);
    init();
    while (t--) {
        int a, b;
        scanf("%d%d", &a, &b);
        printf("%lld\n",
               (ll)2 * fact[a - 1] * infact[b] % mod * infact[a - b - 1] % mod);
    }
    return 0;
}

G - Super Subarray

In this problem, subarray is defined as non-empty sequence of consecutive elements.

We define a subarray as Super Subarray if the summation of all elements in the subarray is divisible by each element in it.

Given an array a of size n, print the number of Super Subarrays in a.

Input
The first line of the input contains a single integer T (1 ≤ T ≤ 100), the number of test cases.

The first line of each test case contains one integer n (1 ≤ n ≤ 2000), the length of array a.

The second line of each test case contains the sequence of elements of the array a1, a2, …, an (1 ≤ ai ≤ 109), ai is the i-th element of the array a.

Output
For each test case, print a single integer that represents the number of Super Subarrays in a, on a single line.

Example
Input

2
5
1 2 3 4 5
5
2 3 4 3 6
Output
6
6
题意: 给一个有n个数的数组,求给数组中有几个超级数组,超级数组的意思是:子区间的区间和是能整除该区间的每一个数。问有几个超级数组。
思路: 直接处理出前缀和,在循环中枚举每种情况的区间,并求出区间的最小公倍数,只要该区间和,能整除这个区间的最小公倍数,就是超级数组。
也可以利用前缀和+线段树处理出区间和和区间最小公倍数,再直接枚举。(虽说有点多此一举)

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e3 + 10;
ll a[N], sum[N];

ll lcm(ll a, ll b) { return a / __gcd(a, b) * b; }

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        int n, res = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            sum[i] = sum[i - 1] + a[i];
        }
        
        for (int i = 1; i <= n; i++) {
            ll t = a[i];
            for (int j = i; j <= n; j++) {
                t = lcm(t, a[j]);
                ll num = sum[j] - sum[i - 1];
                if (t > sum[n]) {
                    break;
                } else if (num % t == 0) {
                    res++;
                }
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

顺便练一下,用线段树写区间最小公倍数

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
#define ll long long
struct node {
    int l,r;
    ll d;
}tr[N*4];
ll a[N],sum[N];
ll lcm(ll a,ll b) {
    return a/__gcd(a,b)*b;
}
void pushup(int u) {
    tr[u].d=lcm(tr[u<<1].d,tr[u<<1|1].d);
}
void build(int u,int l,int r) {
    if(l==r) {
        tr[u]={l,r,a[l]};
    }
    else {
        tr[u].l=l,tr[u].r=r;
        int mid=l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
ll query(int u,int l,int r) {
    if (tr[u].l >= l && tr[u].r <= r)
        return tr[u].d;
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        ll res=1;
        if(l<=mid) {
            res=query(u<<1,l,r);
        } 
        if(r>mid){
            res=lcm(res,query(u<<1|1,l,r));
        } 
        return res;
    }    
}

int main() {
    int n;
    scanf("%d", &n);
    for(int i=1;i<=n;i++) {
        scanf("%lld",&a[i]);
    }
    build(1,1,n);
    int x,y;
    scanf("%d%d",&x,&y);
    printf("%lld\n",query(1,x,y));
    return 0;
}

To be continued
如果你有任何建议或者批评和补充,请留言指出,不胜感激

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值