zoukankan      html  css  js  c++  java
  • CSUST--4.4排位周赛第七场 (全解)

    emmmm,你们tql,这场比赛蒟蒻的我都完全hold不住

    题目说明:

    对决(线段树|树状数组)

    Rap男孩(DP) 

    创造创造(思维)

    这才是真正的冰阔落(数论)

    喝可乐(二分)    

    比赛链接:http://acm.csust.edu.cn/contest/86/problems

    比赛过后无法提交,请到problem中提交

    对决

    题目大意:给你n种数据,每种数据有两种属性值,力量值$x$和敏捷值$y$,当两个人$a,b$的属性值$x_{a}>=x_{b} , y_{a}<=y_{b}$为激烈的对决,问总共有几种激烈的对决方式。

    Sample Input  

    3
    3 2
    1 1
    2 3
    

    Sample Output 

    1

    emmmm,先搞懂题目在说什么,写个暴力看看是T了还是怎么了:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=2e5+10;
    
    struct node
    {
        int x,y;
    }pt[mac];
    
    int main()
    {
        int n;
        scanf ("%d",&n);
        for (int i=1; i<=n; i++){
            int x,y;
            scanf("%d%d",&x,&y);
            pt[i]=node{x,y};
        }
        int ans=0;
        for (int i=1; i<=n; i++){
            for (int j=i+1; j<=n; j++){
                if ((pt[i].x>=pt[j].x && pt[i].y<=pt[j].y) || (pt[i].x<=pt[j].x && pt[i].y>=pt[j].y))
                    ans++;
            }
        }
        printf ("%d",ans);
        return 0;
    }

    没什么毛病,就是T了,T了一半,A了一半。

    然后。。。大概就是找一个属性大于,另一个属性小于的意思,这个我们可以利用线段树来搞,$x_{a}>=x_{b} , y_{a}<=y_{b}$,即在将y作为坐标,在$y_{b}$之前有几个属性$x$大于他的。这个我们应该不难想到对$x$排序,然后。。。此题结束

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define lson l,mid,rt<<1
    #define rson mid+1,r,rt<<1|1
    const int mac=2e5+10;
    
    struct node
    {
        int x,y;
        bool operator<(const node &a)const{
            return x>a.x;
        }
    }pt[mac];
    int tree[mac<<2];
    
    void update(int l,int r,int rt,int pos,int val)
    {
        if (l==r){
            tree[rt]+=val;
            return;
        }
        int mid=(l+r)>>1;
        if (pos<=mid) update(lson,pos,val);
        else update(rson,pos,val);
        tree[rt]=tree[rt<<1]+tree[rt<<1|1];
    }
    
    int query(int l,int r,int rt,int L,int R)
    {
        if (l>=L && r<=R) return tree[rt];
        int sum=0;
        int mid=(l+r)>>1;
        if (L<=mid) sum+=query(lson,L,R);
        if (R>mid) sum+=query(rson,L,R);
        return sum;
    }
    
    int main()
    {
        int n;
        scanf ("%d",&n);
        for (int i=1; i<=n; i++){
            int x,y;
            scanf("%d%d",&x,&y);
            pt[i]=node{x,y};
        }
        long long ans=0;
        sort(pt+1,pt+1+n);
        for (int i=1; i<=n; i++){
            update(1,n,1,pt[i].y,1);
            if (pt[i].y-1==0) continue;
            ans+=query(1,n,1,1,pt[i].y-1);
        }
        printf ("%lld",ans);
        return 0;
    }
    View Code

     

    Rap男孩

    题目大意:给你n个操作和一个基础值x,要么将当前的值下降a[i],要么上升a[i],但必须在[0,ma]之间,输出最后的最大值,如果无法避免越界,则输出-1

    Sample Input 

    3 5 10               
    5 3 7

    Sample Output 

    10

    emmmm,这是个dp题,转移的方式比较奇特。。。可以理解的是一个格子最多只有[0,ma]种值,那么由于n和ma非常小,我们可以直接枚举第i-1个格子的值,然后由着些值通过-a[i]和+a[i]变成第i个格子的值,那么我们最后直接从大到小扫一遍看看dp[n][i]是否存在就OK了

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    int a[100],dp[60][1010];  
    
    int main()
    {
        int n,x,ma;
        scanf ("%d%d%d",&n,&x,&ma);
        for (int i=1; i<=n; i++)
            scanf ("%d",&a[i]);
        dp[0][x]=1;
        for (int i=1; i<=n; i++){
            for (int j=0; j<=ma; j++){
                if (dp[i-1][j] && j+a[i]<=ma) dp[i][j+a[i]]=1;
                if (dp[i-1][j] && j-a[i]>=0) dp[i][j-a[i]]=1;
            }
        }
        int mark=0,ans=0;
        for (int i=ma; i>=0; i--) 
            if (dp[n][i]) {ans=i,mark=1;break;}
        if (!mark) printf("-1
    ");
        else printf("%d
    ",ans);
        return 0;
    }
    View Code

    创造创造

    题目大意:给你n个坐标(x,y)让你找到一个最小的矩形覆盖他们,问最小矩形的面积是多少,其中这些坐标的x可以和这些坐标的y互换。(n<=1e5)

    Sample Input 

    4
    4 1
    3 2
    3 2
    1 3

    Sample Output

    1

    感觉题目有点模糊,x和某些y互换了,那么这些y再和一些x互换了,那么就可以看做是x和x互换了,就是不知道这样是否符合规则,不过看一下输出,发现是整数,那么说明这样是符合规则的,不然的话并不能保证最小矩形是整数。那么也就是说这些x,y的属性是没什么卵用的,我们直接2n个数值存在一起排个序,而要挑出n个做x坐标,n个做y坐标,那么就很明显了,前n个一组a,后n个一组b就完事了,否则的话,如果在a中混进一个b组的,那么毫无疑问面积x坐标的极差会拉大,而同时,b组中也一定会混进a组的,y坐标的极差也会拉大,那么就会导致整个矩形的面积变大,所以这就是一个最小的矩形了。

    以下是AC代码;

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=1e5+10;
    
    int a[mac<<1];
    
    int main()
    {
        int n;
        scanf ("%d",&n);
        for (int i=1; i<=2*n; i++){
            scanf ("%d",&a[i]);
        }
        sort(a+1,a+1+2*n);
        printf ("%lld
    ",1LL*(a[n]-a[1])*(a[n*2]-a[n+1]));
        return 0;
    }
    View Code

    这才是真正的冰阔落

    题目大意:有n个人买可乐,每瓶50元,每个人的付钱方式只能是50元,100元和银行卡,商家没有零钱不找零,同时银行卡有足够的钱,但他们都只能为自己付款,不能代付,问最后收费处还剩L到R张50元的合理方案数(每个人对商家没有欠款),对p取模。输入n,p,l,r(n,l,r<=1e5,p<=2e9)

    Sample Input

    4 100 0 4

    Sample Output

    35

    emm,看到这题,首先我想到了前缀和,他需要的是L到R张50元合理的方案数,那么我们将每种情况的方案数算出来就好了,然后将L到R区间里面的加起来就完事了。至于每种情况怎么算,但我可能语文太lj了,这里隐含了很多条件我都没看出来,一是商家没有零钱的时候不会接受100元的钱,二是有零钱的时候接受100元的钱并退回50元零钱。。。而我一直以为商家不找零,也会接受100元的,剩下的50元就被吞了,于是得出了了$C_n^i imes 2^{n-i}$($i$表示剩下i张50元)的结果。。。。然后我去群里质疑了。。。然后我就被喷了QAQ

    我们设当下已经有x个人拿出50元,y个人拿出了100元,$xin [0,n],yin [0,frac{n}{2}]$,而且在每个时刻一定有都有$y<=x$,剩下的就是卡的数量,我们设为z,而很明显,x和y的组合的数量就是卡特兰数的合法路径数即不越过x=y这条线的数量,即:$C_{x+y}^x-C_{x+y}^{x-1}$。我们枚举x与y的总和,得到当x+y为某个值(记为k)的时候x,y的组合数量(num)有多少,而剩下的z即有组合$C_n^{n-k}=C_n^k=use$个那么该枚举为$num imes use$个。

    枚举总和的时候,也需要枚举x的值,那么时间就远远不够了,所以我们需要简化一下,剩余$i$张50元,那么也就是说有(k-i)/2张50元和(k-i)/2张100元的抵消了,剩下了$i$张50元,由于[L,R]为连续整数区间,每个相邻的数相差1,那么对于每个枚举的x,y的组合数数量从L到R就会有:$C_k^{frac{k-L}{2}}-C_k^{frac{k-L}{2}-1}+C_k^{frac{k-L}{2}-1}-C_k^{frac{k-L}{2}-2}+...+C_k^{frac{k-R}{2}}-C_k^{frac{k-R-1}{2}}$

    那么就可以得到:$C_k^{frac{k-L}{2}}-C_k^{frac{k-R-1}{2}}$,那么接下来就是枚举k从0到n了将每个答案的ans加在一起,最后答案也就是$sum_{k=0}^{n}(C_k^{frac{k-L}{2}}-C_k^{frac{k-R-1}{2}}) imes C_n^k$

    不过还没结束。。。。接下来我们还要想想组合数对p取模的问题,由于p不一定是素数,而n不是非常大,所以可以把每个组合数展开来求,求出每个分子分母的质因子及其次数,然后约分之后,用快速幂求出整个组合数。我们可以预处理出1到n中每个数的质因子及其次数,即node num[i][j]{pos,cnt}表示$i$质因子$pos$有$cnt$个但可惜的是1e5以内的素数大概有1e4个,预处理的复杂度就是$1e5 imes 1e4$直接爆炸。。。。

    所以这里还有另一种做法就是利用欧拉函数求出所有和p互质的数,然后把所有数拆为不含p因子的数和含p因子的数,然后前者做正常的乘法,除法。后者做加,减法,最后统一快速幂乘。

    即对于一个组合数$C_k^m=frac{k!}{m!(k-m)!}$取模就是$k!*inv(m!)*inv((k-m)!)$所以我们要求的就是不含p公因子的阶乘的逆元

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define debug printf ("ciaodddf")
    #define ll long long
    const int mac=1e5+10;
    
    int n,mod,p[mac],cnt;
    int num[mac][50],f[mac],inv[mac];
    
    ll qick(ll a,ll b)
    {
        ll ans=1;
        while (b){
            if (b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    
    void init()
    {
        int phi=mod,use=mod;//phi表示p以内和p互质的数量
        for (int i=2; i<=use/i; i++){
            if (use%i==0){
                p[++cnt]=i;//p的素数因子i
                phi=phi/i*(i-1);
                while (use%i==0) use/=i;
            }
        }
        if (use>1) phi=phi/use*(use-1),p[++cnt]=use;
        //欧拉函数到此结束
        f[0]=inv[0]=1;
        for (int i=1; i<=n; i++){//计算阶乘的在mod下的非公因子的逆元
            int x=i;
            for (int j=1; j<=cnt; j++){
                num[i][j]=num[i-1][j];
                while (x%p[j]==0){
                    num[i][j]++;//i,mod含公共素因子p[j]有多少个
                    x/=p[j];
                } 
            }
            f[i]=1LL*f[i-1]*x%mod;//非公因子的乘积
            inv[i]=qick(f[i],phi-1);//f[i]和mod互质,利用欧拉定理求f[i]的逆元
            //printf ("%d %d 
    ",f[i],inv[i]);
        }
    }
    
    ll C(int k,int m)
    {
        if (m<0 || k<0 || k<m) return 0;
        if (m==0) return 1;
        ll ans=(1LL*f[k]*inv[m]%mod)*inv[k-m]%mod;//C(k,m)=(k!)/(m!(k-m)!)
        //debug;
        for (int i=1; i<=cnt; i++){
            ans=ans*qick(p[i],num[k][i]-num[m][i]-num[k-m][i])%mod;//将少乘的公共素因子乘回去
        }
        //printf ("%lld ",ans);
        return ans;
    }
    
    int main()
    {
        int P,l,r;
        scanf ("%d%d%d%d",&n,&P,&l,&r);
        mod=P;
        init();
        ll ans=0;
        for (int k=0; k<=n; k++){
            ans+=((C(k,(k-l)>>1)-C(k,((k-r-1)>>1))+mod)%mod)*C(n,k)%mod;
            ans%=mod;
        }
        printf ("%lld
    ",ans);
        return 0;
    }
    View Code

    喝可乐   

    题目大意:给你一个长度为n的序列,你可以操作k次,每次将最大的数匀1给最小的数,问k次操作之后的极差最小值是多少

    Sample Input 

    4 100
    1 1 10 10
    

    Sample Output 

    1

    emmm,出的题都是奇奇怪怪的。。。极差最小值,也就是最大值和最小值之差,那么我们二分就可以了,二分k天后最大值允许达到的最小值和最小值允许达到的最大值。然后这题就over了。。。至于怎么判断其是否允许也不是很难,最小值最大也就在平均值徘徊,最大值最小也是一样的,我们将原大于最大值的数全部减去最大值求得下降到该值需要多久,判断是否大于k天即可

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int mac=5e5+10;
    const int inf=1e9+10;
    
    int a[mac],n,k;
    long long sum=0,tot;
    
    int ok_max(int x)
    {
        int p=sum/n;
        if (sum%n) p++;
        if (x<p) return 0;
        long long days=0;
        for (int i=1; i<=n; i++){
            if (a[i]>x) days+=a[i]-x;
            else break;
        }
        if (days>tot) return 0;
        return 1;
    }
    
    int ok_min(int x)
    {
        int p=sum/n;
        if (x>p) return 0;
        long long days=0;
        for (int i=1; i<=n; i++){
            if (a[i]<x) days+=x-a[i];
        }
        if (days>tot) return 0;
        return 1;
    }
    
    bool cmp(int x,int y){return x>y;}
    
    int main()
    {
        scanf ("%d%d",&n,&k);
        tot=k;
        int l=inf,r=-inf;
        for (int i=1; i<=n; i++){
            scanf("%d",&a[i]);
            l=min(a[i],l);
            r=max(a[i],r);
            sum+=a[i];
        }
        sort(a+1,a+1+n,cmp);
        int ans_max,ans_min;
        int l1=l,r1=r,mid;
        while (l1<=r1){
            mid=(l1+r1)>>1;
            if (ok_max(mid)){
                ans_max=mid;//最大值最小是多少
                r1=mid-1;
            }
            else l1=mid+1;
        }
        int l2=l,r2=r;
        while (l2<=r2){
            mid=(l2+r2)>>1;
            if (ok_min(mid)){
                ans_min=mid;//最小值最大是多少
                l2=mid+1;
            }
            else r2=mid-1;
        }
        printf("%d
    ",ans_max-ans_min);
        return 0;
    }
    View Code
    路漫漫兮
  • 相关阅读:
    JAVA this
    JAVA static关键字
    子类实例化 super
    TCP/IP概述
    Java多态
    植物大战僵尸:寻找阳光掉落Call调用
    JVM总结+个人整理补充--转
    已知微分方程通解求微分方程
    Redis安装过程中的问题
    对称阵和反对称阵
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/12635869.html
Copyright © 2011-2022 走看看