2018 Multi-University Training Contest 4

6333  Problem B. Harvest of Apples(莫队)

题意

求C(n,m)到C(n,0)的求和

 

题解

S(n,m)=S(n,m-1)+C(n,m)=S(n-1,m)*2-C(n-1,m)(后面一个根据C(n,m)递推式展开就知道了),由此我们可以用莫队实现,O(1)转移

 

代码

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define N 100005
#define mod 1000000007
using namespace std;
typedef long long ll;
ll a[N],pos[N],f[N],inv[N],len;
struct node
{
    ll l,r,id;
    node(){}
    node(ll l,ll r,ll id)
    {
        this->l=r;
        this->r=r;
        this->id=id;
    }
}q[N];
ll ansa[N];
bool cmp(node a,node b)
{
    return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;
}
ll qmi(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b%2)ans=ans*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return ans;
}
ll C(ll n,ll m)
{
    return f[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    ll n=100000,m,INV=(mod+1)/2;
    f[0]=1;
    for(ll i=1;i<=n;i++)
        f[i]=(f[i-1]*i)%mod;
    for(ll i=0;i<=n;i++)
        inv[i]=qmi(f[i],mod-2);
    scanf("%lld",&m);
    len=(ll)sqrt((double)n+0.1);
    for(ll i=1;i<=n;i++)
        pos[i]=(i-1)/len+1;
    for(ll i=0;i<m;i++)
    {
        scanf("%lld%lld",&q[i].r,&q[i].l);
        q[i].id=i;
    }
    sort(q,q+m,cmp);
    ll ans=1;
    ll l=0,r=1;
    for(ll i=0;i<m;i++)
    {
        if(r<q[i].r)
        {
            for(;r<q[i].r;r++)
                ans=(2*ans-C(r,l)+mod)%mod;
        }
        if(q[i].l<l)
        {
            for(;l>q[i].l;l--)
                ans=(ans-C(r,l)+mod)%mod;
        }
        if(r>q[i].r)
        {
            for(r=r-1;r>=q[i].r;r--)
                ans=(ans+C(r,l))*INV%mod;
            r++;
        }
        if(q[i].l>l)
        {
            for(l=l+1;l<=q[i].l;l++)
                ans=(ans+C(r,l))%mod;
            l--;
        }
        ansa[q[i].id]=ans;
    }
    for(ll i=0;i<m;i++)
        printf("%lld\n",ansa[i]);

}

6336  Problem E. Matrix from Arrays

题意

int cursor = 0;

for (int i = 0; ; ++i) {
    for (int j = 0; j <= i; ++j) { 
        M[j][i - j] = A[cursor];
        cursor = (cursor + 1) % L;
    }
}

然后求M的子矩阵的和

题解

所以预处理左上角2L X 2L的矩阵的二维前缀和, O(1)回答询问。
 

代码

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
ll M[1111][1111];
ll A[11];
int L;
ll get_num(ll x, ll y)
{
    ll n = x + y;
    n = (1 + n)*n / 2;
    n = (n + x + 1) % L;
    if (n == 0)
        n += L;
    return A[n - 1];
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &L);
        for (int i = 0; i < L; i++)
            scanf("%lld", &A[i]);
        int cursor = 0;
        for (int i = 0; i < 1000; ++i) {
            for (int j = 0; j <= i; ++j) {
                M[j][i - j] = A[cursor];
                cursor = (cursor + 1) % L;
            }
        }
        ll xun;
        if (L % 2)xun = L;
        else xun = 2 * L;
        int q;
        scanf("%d", &q);

        while (q--)
        {
            ll sum = 0;
            ll x1, x2, y1, y2;
            scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
            ll zong = x2 - x1 + 1;
            ll heng = y2 - y1 + 1;
            ll zong1 = zong / xun;
            ll heng1 = heng / xun;
            ll yu_heng = heng % xun;
            ll yu_zong = zong % xun;
            ll fang = (zong / xun)*(heng / xun);

            ll sum_fang = 0;
            if (fang)
                for (int i = 0; i < xun; i++)
                    for (int j = 0; j < xun; j++)
                        sum_fang += M[i][j];
            sum_fang *= fang;

            ll sum_zong = 0;
            if (zong1)
                for (int i = 0; i < xun; i++)
                    for (int j = 0; j < yu_heng; j++)
                        sum_zong += get_num(x1 + i, y1 + j);
            sum_zong = zong1 * sum_zong;

            ll sum_heng = 0;
            if (heng1)
                for (int i = 0; i < yu_zong; i++)
                    for (int j = 0; j < xun; j++)
                        sum_heng += get_num(x1 + i, y1 + j);
            sum_heng = sum_heng * heng1;

            ll sum_yu = 0;
            for (int i = 0; i < yu_zong; i++)
                for (int j = 0; j < yu_heng; j++)
                    sum_yu += get_num(i + x1, j + y1);
            sum += sum_yu;
            sum += sum_fang;
            sum += sum_heng;
            sum += sum_zong;
            printf("%lld\n", sum);
        }
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值