10.7 test solution.

来源:互联网 发布:mac 网站编辑 编辑:程序博客网 时间:2024/06/08 16:32

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.in count.out 10 2
2 3 3

【数据说明】

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


solution

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

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

  • 比如说有两个数,24,如果一个数是 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 25ll 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。
第一行两个数nk
第二行,n个数。(0每个数<231

【输出】

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

【输入输出样例】

kth.in kth.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

  • 发现这个式子的两边形式差不多,所以设数组cc[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 100010ll 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,Ca1表示第一个数,A,B,C用来生成其余n1个数。a[i]=(a[i1]A+B)modC
1n1000000,0a1,A,B,C1000000000

【输出】

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

【输入输出样例】

sum.in sum.out 3 3 1 1 10 63

【样例解释】

三个数为 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]

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

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 1000000007struct 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::MAXNll 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;}
原创粉丝点击