10.7 test solution.

1.计数 (count.cpp/c/pas)

时间限制:

1s

内存限制:

256MB

【问题描述】

给出 m 个数 a[1],a[2],,a[m]
1n 中有多少数不是 a[1],a[2],,a[m] 的倍数。

【输入】

输入文件名为count.in。
第一行,包含两个整数: n,m
第二行,包含 m 个数,表示a[1],a[2],,a[m]

【输出】

输出文件名为count.out。
输出一行,包含 1 个整数,表示答案

【输入输出样例】

count.incount.out
10 2
2 3
3

【数据说明】

对于60%的数据, 1n106
对于另外20%的数据, m=2
对于100%的数据, 1n109,0m20,1a[i]109


solution

  • 一眼秒掉的题,就是一个小学生容斥。

  • 还有就是一个小优化,如果 m 个数中存在倍数关系,那就取最小的那个

  • 比如说有两个数,2 4 ,如果一个数是 4 的倍数,那他也肯定是 2 的倍数,所以只求 2 的就好了。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define MAXM 25

ll a[MAXM];

ll gcd(ll a,ll b) {
    return b==0?a:gcd(b,a%b);
}

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

bool cmp(ll a,ll b) {
    return a>b;
}

int main() {
    int n,m;
    input(n),input(m);
    for(int i=0;i<m;i++)
        input(a[i]);
    sort(a,a+m);
    if(a[1]==1) {
        printf("0");
        return 0;
    }
    for(int i=0;i<m;i++) {
        if(a[i]==0) continue;
        for(int j=i+1;j<m;j++)
            if(a[j]%a[i]==0)
                a[j]=0;
    }
    sort(a,a+m,cmp);
    for(int i=0;i<m;i++)
        if(a[i]==0) {
            m=i;
            break;
        }
    //上面就是我说的那个优化,下面是容斥,可能用二进制写有点恶心,不过短小精悍
    int ans=n,sum;
    ll x;
    for(int s=1;s<1<<m;s++) {
        sum=0,x=1;
        for(int i=0;i<m;i++)
            if(s&(1<<i)) {
                x=lcm(x,a[i]);
                sum++;
            }
        if(x>n) continue;
        if(sum&1) ans-=n/x;
        else ans+=n/x;
    }
    printf("%d",ans);
    return 0;
}

2.第k大区间(kth.cpp/c/pas)

时间限制:

1s

内存限制:

256MB

【问题描述】

定义一个长度为奇数的区间的值为其所包含的的元素的中位数。
现给出 n 个数,求将所有长度为奇数的区间的值排序后,第 K 大的值为多少。

【输入】

输入文件名为kth.in。
第一行两个数 n k
第二行, n 个数。(0每个数 <231 <script type="math/tex" id="MathJax-Element-24"><2^{31}</script>)

【输出】

输出文件名为kth.out。
一个数表示答案。

【输入输出样例】

kth.inkth.out
4 3
3 1 2 4
2

【样例解释】

[l,r] 表示区间 lr 的值
[1,1]3
[2,2]1
[3,3]2
[4,4]4
[1,3]2
[2,4]2

【数据说明】

对于 30% 的数据, 1n100
对于 60% 的数据, 1n300
对于 80% 的数据, 1n1000
对于 100% 的数据, 1n100000,k 奇数区间的数


solution

  • 求第 k 大这种题一般是二分答案,所以这道题目也是二分

  • 我们二分中位数 mid,然后问题就转换成了怎样快速求出有多少个长度为奇数的区间中位数 mid

  • 这里利用了一个特别巧妙的做法

  • 二分之后我们把每个数都重新赋值, mid 的数设为 1 <mid的数设为 0

  • 重新赋值之后,如果一个区间的中位数是mid,那这个区间所有数的和应该是 rl+12+1

  • 我们令 sum 为重新赋值之后的前缀和,那二分之后求的也就是满足 sum[r]sum[l1]rl+12+1 的区间个数

  • 整数的离散性:如果 a,bZ ,那么 ab 可以表示为 a>b1

  • 根据上面那个东西,我们可以把式子变成 sum[r]sum[l1]>rl+12

  • 然后再把分母乘过去,移项就可以得到 2sum[l1](l1)<2sum[r]r

  • 发现这个式子的两边形式差不多,所以设数组 c c[i]=2sum[i]i

  • 然后式子就变成了 c[l1]<c[r] ,也就是要求满足 c[l1]<c[r] 的区间的个数

  • 又因为 l1<r ,所以最后求的也就是 c 数组的顺序对个数

  • 最后需要说明的

  • 因为长度为奇数的区间的左右端点l,r奇偶性是相同的,所以 l1,r 的奇偶性是不同的,所以我们需要分奇偶统计答案

  • 求顺序对我用的是树状数组,会求逆序对的应该可以看懂…

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define MAXN 100010

ll k;
int n,m;
int a[MAXN],b[MAXN];
int t[MAXN];

inline int lowbit(int x) {
    return x&(-x);
}

void modify(int x) {
    for(;x<=m;x+=lowbit(x))
        t[x]++;
    return;
}

int query(int x) {
    int ans=0;
    for(;x;x-=lowbit(x))
        ans+=t[x];
    return ans;
}

int c[MAXN],d[MAXN];

int Lower_bound(int x) {
    int l=1,r=m,mid;
    while(l<r) {
        mid=l+r>>1;
        if(x<=d[mid]) r=mid;
        else l=mid+1;
    }
    return l;
}

ll Judge(int mid) {
    c[0]=0;
    for(int i=1;i<=n;i++)
        c[i]=c[i-1]+(a[i]>=mid);
    for(int i=0;i<=n;i++)
        c[i]=c[i]*2-i,d[i+1]=c[i];
    sort(d+1,d+n+2);
    int rank=1;
    for(int i=2;i<=n+1;i++)
        if(d[i]!=d[rank])
            d[++rank]=d[i];
    m=rank;
    ll ans=0;
    for(int i=0;i<=n;i++)
        c[i]=Lower_bound(c[i]);
    memset(t,0,sizeof(t));
    for(int i=0;i<=n;i++)
        if(i&1) modify(c[i]);
        else ans+=(ll)query(c[i]-1);
    memset(t,0,sizeof(t));
    for(int i=0;i<=n;i++)
        if((i&1)==0) modify(c[i]);
        else ans+=(ll)query(c[i]-1);
    return ans;
}

int main() {
    input(n),input(k);
    for(int i=1;i<=n;i++)
        input(a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    int rank=1;
    for(int i=2;i<=n;i++)
        if(b[i]!=b[rank])
            b[++rank]=b[i];
    int l=1,r=rank,mid;
    ll result;
    while(l<r) {
        mid=(l+r>>1)+1;
        result=Judge(b[mid]);
        if(result==k) {
            printf("%d\n",b[mid]);
            return 0;
        } else if(result>k) l=mid;
        else r=mid-1;
    }
    printf("%d\n",b[l]);
    return 0;
}

3.区间求和(sum.cpp/c/pas)

时间限制:

2s

内存限制:

256MB

【问题描述】

n 个数,给定一个 k,求所有长度大于等于 k 的区间中前 k 大数的总和。这样就比较简单相信大家都会,所以此题要求当 k=1n 的总和,即求

k=1ni=1nk+1j=i+k1nk

【输入】

输入文件名为sum.in。
输入五个数 n,a1,A,B,C a1 表示第一个数, A,B,C 用来生成其余 n1 个数。 a[i]=(a[i1]A+B)modC
1n1000000,0a1,A,B,C1000000000

【输出】

输出文件名为sum.out。
一个数表示答案,最后答案对 1000000007 取模。

【输入输出样例】

sum.insum.out
3 3 1 1 1063

【样例解释】

三个数为 3,4,5
K=1:[1,1]=3,[1,2]=[2,2]=4,[1,3]=[2,3]=[3,3]=5
(表示各个区间在 k=1 时的答案)
K=2:[1,2]=7,[2,3]=[1,3]=9
K=3:[1,3]=12

【数据说明】

对于 30% 的数据, 1n100
对于 60% 的数据, 1n300
对于 80% 的数据, 1n1000
对于 100% 的数据, 1n1000000


solution

  • 假设我们已经知道了区间 [l,r] 的答案是 ans ,考虑新加入一个数 a[r+1] 对答案会造成什么影响

  • c [l,r] a[r+1] 的数的个数

  • sum 为区间 [l,r] >a[r+1] 的数的和

  • 那么区间 [l,r+1] 的答案就是 ans+(c+1)a[r+1]+sum

  • f[i] 表示以 i 为右端点的所有的区间的答案之和

  • f[i]=l=1ians[l,i]

  • 把上面推的式子代进去,可以得到

    f[i]=f[i1]+ia[i]+a[j]a[i]ja[j]+a[k]<a[i]ka[i]

  • 最后 ans=i=1nf[i]

  • 然后上面那个东西可以用数据结构来维护,这里我选择树状数组

code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

template<typename T>
void input(T &x) {
    x=0; T a=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar())
        if(c=='-') a=-1;
    for(;c>='0'&&c<='9';c=getchar())
        x=x*10+c-'0';
    x*=a;
    return;
}

#define mod 1000000007

struct Fenwick_Tree {
    static const int MAXN=1000010;
    ll tree[MAXN];
    int n;
    Fenwick_Tree() {
        memset(tree,0,sizeof(tree));
    }
    int lowbit(int x) {
        return x&(-x);
    }
    void Modify(int k,ll x) {
        for(;k<=n;k+=lowbit(k))
            tree[k]+=x,tree[k]%=mod;
        return;
    }
    int query(int k) {
        int ans=0;
        for(;k;k-=lowbit(k))
            ans+=tree[k],ans%=mod;
        return ans%mod;
    }
    int query(int l,int r) {
        return (query(r)-query(l-1)+mod)%mod;
    }
};

Fenwick_Tree t[2];

#define MAXN Fenwick_Tree::MAXN

ll a[MAXN],b[MAXN];
int Rank;
int f[MAXN];

int Binary_Chop(ll x) {
    int l=1,r=Rank,mid;
    while(l<r) {
        mid=l+r>>1;
        if(x<=b[mid]) r=mid;
        else l=mid+1;
    }
    return l;
}

int main() {
    ll n,A,B,C;
    input(n),input(a[1]),
    input(A),input(B),input(C);
    A%=C,B%=C;
    a[1]%=C;
    for(int i=2;i<=n;i++)
        b[i]=a[i]=(a[i-1]*A+B)%C;
    b[1]=a[1];
    sort(b+1,b+n+1);
    Rank=1;
    for(int i=2;i<=n;i++)
        if(b[i]!=b[Rank])
            b[++Rank]=b[i];
    f[0]=0;
    t[0].n=t[1].n=n;
    ll sum;
    for(int i=1,pos;i<=n;i++) {
        pos=Binary_Chop(a[i]);
        sum=(ll)i*a[i]%mod;
        sum+=(ll)t[0].query(pos)*a[i]%mod;
        sum+=(ll)t[1].query(pos+1,n)%mod;
        f[i]=(ll)(f[i-1]+sum)%mod;
        t[0].Modify(pos,i);
        t[1].Modify(pos,i*a[i]);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
        ans=(ll)(ans+f[i])%mod;
    printf("%d\n",ans%mod);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值