9.30测试

真 头皮发麻

头一次写题写到恶心

怕是以后都不会再去推垃圾数学了

(数论算个屁,打表找规律)

A:

给定一个长度为 n 的序列 a 以及常数 k,序列从 1 开始编号。记

(l, r) = ∑ ai − max {ai}

,求合法的正整数对 (l, r) 的数量,满足 1 ≤ l < r n,且 k| f (l, r)。

对于 30% 的数据,n ≤ 3000;

对于另外 20% 的数据,数列 a 为随机生成;

对于 100% 的数据,1 ≤ n ≤ 3 × 10^5, 1 ≤ k ≤ 10^6, 1 ≤ ai ≤ 10^9。

看一眼是一个O(n^2)的暴力对吧,然而数据范围只允许你O(nlogn)

所以考虑O(nlogn)的暴力

带了一个log的暴力自然会让你想到二分(在这里称为“分治”更加准确)

在search(l,r)时考虑起点在前半边,终点在后半边的方案数

首先扫一遍后半边,记录下以mid为起点的每个序列可能出现的最大值以及他们的位置

将它与后半边可能出现的最大值进行比较

同时继续更新saveans

(感觉自己在考场上最后3min调出来真是万幸,jry附体)

同时用saveans保存一下最大值在后半边时的情况数

然后扫一遍前半边(从后往前),对于每一次的值,

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
int a[300500],saveans[1000500][2],//0表示最大值在右边的这一段,1表示最大值在左边这一段
    maxpos[300500],maxque[300500],//maxpos:到达当前位置时最大值在哪里,maxque:加入maxpos中出现过的值
    n,k,sum[300500];
long long ans=0;

void bisearch(int l,int r)
{
    if (l==r) return;
    int mid=(l+r)/2,i,cnt=0,maxd=0;
    sum[mid]=0;maxque[0]=0;
    for (i=mid+1;i<=r;i++)
    {
        if (a[i]>a[maxque[cnt]]) maxque[++cnt]=i;
        sum[i]=(sum[i-1]+a[i]%k)%k;
        saveans[(sum[i]-a[maxque[cnt]]%k+k)%k][0]++;maxpos[i]=maxque[cnt];
    }
    maxd=0;maxque[cnt+1]=r+1;
    int s=0,p=1,p1=mid+1;
    for (i=mid;i>=l;i--)
    {
        s=(s+a[i]%k)%k;maxd=max(maxd,a[i]);
        while ((p<=cnt) && (maxd>=a[maxque[p]])) p++;
        while (p1<maxque[p]) {saveans[(sum[p1]-a[maxpos[p1]]%k+k)%k][0]--;saveans[sum[p1]][1]++;p1++;}
        ans+=saveans[(k+maxd%k-s)%k][1];
        if (p<=cnt) ans+=saveans[(k-s)%k][0];
    }
    for (i=mid+1;i<p1;i++) saveans[sum[i]][1]--;
    for (i=p1;i<=r;i++) saveans[(sum[i]-a[maxpos[i]]%k+k)%k][0]--;
    bisearch(l,mid);
	bisearch(mid+1,r);
}

int main()
{
    freopen("interval.in","r",stdin);
    freopen("interval.out","w",stdout);
    scanf("%d%d",&n,&k);
    int i;
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    bisearch(1,n);
    printf("%lld",ans);
    return 0;
}

B有 m 个在 [0, 2n) 内均匀随机取值的变量,求至少有两个变量取值相同的概率。为了避免精度误差,假设你的答案可以表示成 a  的形式(其中 (ab) = 1),你需要输出 a b 对 10^6 + 3 取模后的值。

真 辣鸡数学题

正难则反,概率可以被表示为1- \frac{\prod^{i=m-1}_{i=0}2^n-i}{2^{nm}}

直接对这个式子求值明显是会猝死的,所以考虑优化

快速幂自不用说

考虑约分时只可能约去2,因此考虑上面含有2 因子数如何求出

注意到一个性质:2^n-xx含有2的因子数相同(假设x=2^p*q(p,q均为正整数且,2^{p+1}不是x的约数),

则q是奇数,且2^n-x=2^n-2^p*q=2^p*(2^{n-p}-q)

所以原命题得证股原分式的分子含有的2的质因子的个数与(m-1)! 相同

一个很关键的地方被解决了

。。。。。。等等,分子的值怎么求啊

注意到模数很小,只有10^6,且分子也对这个数取模,又注意到分子各式之间相差1,因此当m大于10^6时,分子必有一项是模数的倍数,所以此时分子模出来为0

好像就可以解决了

。。。。。。你说除法啊,直接乘逆元不就好了

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<queue>
#include<map>
using namespace std;
const long long maxd=1e6+3,mind=500002;
long long n,m;
long long pow(long long x,long long y)
{
    long long ans=1,now=x;
    while (y)
    {
        long long tmp=y%2;y/=2;
        if (tmp) ans=(ans*now)%maxd;
        now=(now*now)%maxd;
    }
    return ans;
}

int main()
{
    freopen("random.in","r",stdin);
    freopen("random.out","w",stdout);
    scanf("%lld %lld",&n,&m);
    if (log2(m)>n) {printf("1 1\n");return 0;}
    long long tmp=pow(2,n),b=pow(tmp,m),a=1,i;
    for (i=0;i<m;i++)
    {
        a=(a*(tmp-i))%maxd;
        if (a==0) break;
    }
    m--;long long t=n;
    for (i=2;i<=m;i<<=1) t+=m/i;
    a=(a*pow(mind,t))%maxd;
    b=(b*pow(mind,t))%maxd;
    printf("%lld %lld",(b-a+maxd)%maxd,b);
    return 0;
}

C:对于一个长度为 n,且下标从 1 开始编号的序列 a,我们定义它是「合法的」,当且仅当它满足以下条件:

  • a1 = 1
  • 对于 i ∈ [1, n),ai ai+1 ≤ ai + 1 且 ai+1 为正整数;
  • 对于任意在 a 中出现过的数 v,记它的出现次数为 s,则 2 ≤ s ≤ 5。

给定一个长度为 n 的序列 a,其中有一些位置为 0,你需要在这些位置上任意填数, 使得 a 成为一个合法的序列,并且最大化 an 的值。

其实C才是最简单的

我们记录两个数组:up与down,up记录当前位置的最大值以及该最大值在序列的长度,down则记录最小值

一个很明显的贪心:up逢二进一,down逢五进一

当前序列若有值,则将这个值直接赋给up与down

最后判断一下如果最后的up与down矛盾就输出-1

输出方案的话从后往前扫一遍就可以了

#include<iostream>
#include<string>
#include<string.h>
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
struct node{
    int num,len;
}up[200500],down[200500];
int a[500500],n,t[500500];
int main()
{
    freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout); 
	scanf("%d",&n);
    int i;
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    if (a[1]>1) {printf("-1\n");return 0;}
    up[1].num=1;up[1].len=1;down[1].num=1;down[1].len=1;
    for (i=2;i<=n;i++)
    {
        up[i]=up[i-1];down[i]=down[i-1];
        up[i].len++;down[i].len++;
        if (up[i].len>2) {up[i].len=1;up[i].num++;}
        if (down[i].len>5) {down[i].len=1;down[i].num++;}
        if (a[i])
        {
            if (up[i].num>a[i]) {up[i].num=a[i];up[i].len=2;}
                else if (up[i].num==a[i]) up[i].len=min(up[i].len,2);
            if (down[i].num<a[i]) {down[i].num=a[i];down[i].len=1;}
            if ((up[i].num<a[i]) || (down[i].num>a[i])) {printf("-1\n");return 0;}
        }
    }
    if (up[n].len==1) {up[n].num--;up[n].len=up[n-1].len+1;}
    if (up[n].num<down[n].num) {printf("-1\n");return 0;}
    a[n]=up[n].num;
    memset(t,0,sizeof(t));t[a[n]]++;
    for (i=n-1;i>=1;i--)
    {
        if (!a[i])
        {
            int tmp=min(a[i+1],up[i].num);
            if (t[tmp]==5) tmp--;
            a[i]=tmp;
        }
        t[a[i]]++;
    }
    printf("%d\n",a[n]);
    for (i=1;i<=n;i++) printf("%d ",a[i]);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值