zoukankan      html  css  js  c++  java
  • 2020.2.15模拟赛

    剑与魔法

    【问题描述】

    万老师听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。

    闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的 事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件 发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事 件会发生。老师希望获得最多的金钱,所以求助于你。

    【输入】

    第一行一个数N,表示输入文件有多少行。 接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去 事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。

    【输出】

    第一行一个整数,最多金钱数目。

    若不可能则输出-1。

    【输入输出样例】

    dragons.in

    5

    c 10

    c 12

    e 2

    c 1

    e 2

    dragons.out

    13

    【数据说明】

    30%的数据满足 N<=20

    60%的数据满足 N<=1,000

    100%的数据满足 N<=200,000

    每次涨RP事件赏金不超过10,000

    穿越事件的要求不超过200,000 

    用堆(优先队列)实现贪心策略。尽量从堆中取出金钱。

    #include<bits/stdc++.h>
    using namespace std;
    priority_queue<int> q;
    int main()
    {
        int n,h=0;
        scanf("%d",&n);
        for (int i=1;i<=n;i++)
        {
            char c;
            cin>>c;
            int x;
            scanf("%d",&x);
            if (c=='c')
            {
                x=-x;
                q.push(x);
                h++;
            }
            else
            {
                if (i!=n)
                {
                    for (int i=1;i<=h-x+1;i++)
                    q.pop();
                    if (h>=x) h=x-1;
                }
            }   
        }
        int ans=0;
        for (int i=1;i<=h;i++)
        {
            int x;
            x=q.top();
            q.pop();
            ans-=x;
        }
        printf("%d",ans);
    }

    矩形

    【问题描述】

    因为对polo忍无可忍, dzf使用圣剑在地上划出了许多纵横交错的沟壑来泄愤。这些沟壑都严格与X轴平行或垂直。 polo嘲笑了dzf无聊的行为,然后做了一件更加无聊的事。他蹲下来数这些沟壑的条数。数着数着,polo意识到一个问题,那就是因为圣剑的威力太大,划出的沟壑太多,地面就会塌陷。而如果两条水平的沟壑和两条垂直的沟壑相交组成了一个矩形,那么塌陷的危险就会进一步增加。现在polo已经数了n条沟壑,他想知道这些沟壑组成了多少个矩形。

    【输入】

    第一行一个数n,接下来每行4个数x1,y1,x2,y2,表示沟壑的两个端点(x1,y1),(x2,y2)

    【输出】

    一个数,组成的矩形个数。

    【输入输出样例1】

    rect.in

    4

    0 0 1 0

    0 0 0 1

    1 1 1 -1

    1 1 0 1

    rect.out

    1

    【输入输出样例2】

    rect.in

    8

    1 0 4 0

    2 1 2 0

    0 0 0 3

    2 2 2 3

    3 3 3 -1

    0 3 4 3

    4 1 -1 1

    3 2 -1 2

    rect.out

    6

    【数据说明】

    对于30%的数据,1<=n<=100

    对于60%的数据,1<=n<=600

    对于100%的数据,1<=n<=2000,坐标绝对值小于10^9,任意两条与X轴水平的沟壑之间没有交点,任意两条与X轴垂直的沟壑没有交点。

    一开始打了个暴力O(n3)的算法,果然只拿了55分。

    枚举两条垂直的线段,枚举一条水平线段,再枚举一条水平的与两者相交的线段k,明显能构成C(k,2)=k(k-1)/2个矩形。

    但是O(n3)好像也能过,就优化了一点点吧,减少一点情况。

    放两个代码对比。

    55分代码。

    #include<bits/stdc++.h>
    const int N=200005;
    using namespace std;
    int n,nx,ny;
    struct jsy 
    {
        int x1,y1;
        int x2,y2;
    }x[N],y[N];
    int main() 
    {
        cin>>n;
        for(int i=1;i<=n;i++) 
        {
            int t1,t2,t3,t4;
            cin>>t1>>t2>>t3>>t4;
            if(t2==t4) 
            {
                x[++nx].x1=min(t1,t3);
                x[nx].y1=t2;
                x[nx].x2=max(t1,t3);
                x[nx].y2=t4;
            }
            if(t1==t3) 
            {
                y[++ny].x1=t1;
                y[ny].y1=min(t2,t4);
                y[ny].x2=t3;
                y[ny].y2=max(t2,t4);
            }
        }
        if(n<4) 
        {
            printf("0");
            return 0;
        }
        long long ans=0;
        for(int i=1;i<=ny;i++)
        {
            for(int j=i+1;j<=ny;j++) 
            {
                int t=0;
                for(int k=1;k<=nx;k++) 
                {
                    if(x[k].x1<=y[i].x1&&x[k].x1<=y[j].x1&&x[k].x2>=y[i].x1&&x[k].x2>=y[j].x1&&x[k].y1>=y[i].y1&&x[k].y1>=y[j].y1&&x[k].y1<=y[i].y2&&x[k].y1<=y[j].y2)
                    t++;
                }
                ans+=t*(t-1)/2;
            }
        }   
        cout<<ans;
    }

    100分代码。

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2001;
    struct ahj 
    {
        int a,b,c,d;
    }s[N],t[N];
    int n,t1,t2;
    int q[N][N],w[N][N],c[N]; 
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            int a,b,c,d;
            cin>>a>>b>>c>>d;
            if(b==d)
            {
                if(a>c) swap(a,c);
                s[++t1]=ahj{a,b,c,d};
            }
            else 
            {
                if(b>d) swap(b,d);
                t[++t2]=ahj{a,b,c,d};
            }
        }
        for(int i=1;i<=t1;i++)
        {
            for(int j=1;j<=t2;j++)
            {
                if(s[i].a<=t[j].a&&t[j].a<=s[i].c&&t[j].b<=s[i].b&&s[i].b<=t[j].d)
                {
                    q[i][j]=1;
                    w[i][++c[i]]=j;
                }
            }
        }
        int ans=0;
        for(int i=1;i<t1;i++)
        {
            for(int j=i+1;j<=t1;j++)
            {
                int k=0;
                int x=i,y=j;
                if(c[i]>c[j]) swap(x,y);
                for(int h=1;h<=c[x];h++)
                {
                    if(q[y][w[x][h]]) k++;
                }
                ans+=k*(k-1)/2;
            }
        }
        printf("%d",ans);
        return 0;
    }

    但是正解貌似是线段树。

    线段树+离散化,利用线段树的区间求和来解决,总时间复杂度为O(n2log2n)。

    但是我没有写,没有代码就算了吧。


    数列的GCD

    【问题描述】

    给出一个长度为N的数列{a[n]},1<=a[i]<=M(1<=i<=N)。

    现在问题是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], ..., b[N],满足:

    (1)1<=b[i]<=M(1<=i<=N);

    (2)gcd(b[1], b[2], ..., b[N])=d;

    (3)恰好有K个位置i使得a[i]<>bi

    注:gcd(x1,x2,...,xn)为x1, x2, ..., xn的最大公约数。

    输出答案对1,000,000,007取模的值。

    【输入】

    第一行包含3个整数,N,M,K。

    第二行包含N个整数:a[1], a[2], ..., a[N]。

    【输出】

    输出M个整数到一行,第i个整数为当d=i时满足条件的不同数列{b[n]}的数目mod 1,000,000,007的值。

    【输入输出样例1】

    gcd.in

    3 3 3

    3 3 3

    gcd.out

    7 1 0

    【输入输出样例2】

    gcd.in

    3 5 3

    1 2 3

    gcd.out

    59 3 0 1 1

    【样例1解释】

    当d=1,{b[n]}可以为:(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1)。

    当d=2,{b[n]}可以为:(2, 2, 2)。

    当d=3,因为{b[n]}必须要有k个数与{a[n]}不同,所以{b[n]}不能为(3, 3, 3),满足条件的一个都没有。

    【数据说明】

    对于30%的数据,1<=N<=20, 1<=M<=2。

    对于50%的数据,1<=N,M<=1000。

    对于70%的数据,1<=N,M<=10000。

    对于100%的数据,1<=N,M<=300000, 1<=K<=N, 1<=a[i]<=M。

    可以推出下列公式:

    ans [ d ] = C(s,sum) * ([m/d]-1)^(sum-s)  *  ([m/d])^(n-sum) 

    d表示公倍数,s是与原数列要相同的数的个数(即n-k),sum为在a数列中是d倍数的数的个数。

    注意取模,还有阶乘的处理。

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N=5e5+7,M=1e9+7;
    int n,m,k;
    ll f[N],ans[N];
    int cnt[N];
    ll st(ll x,ll y)
    {
        ll res=1;
        while(y)
        {
            if(y&1) res=(res*x)%M;
            x=x*x%M; 
            y>>=1;
        }
        return res;
    }
    ll C(ll n,ll m)
    { 
        return f[m]*st(f[n],M-2)%M*st(f[m-n],M-2)%M; 
    }
    int main()
    {
        int a;
        cin>>n>>m>>k;
        for(int i=1;i<=n;i++) 
        {
            scanf("%d",&a);
            cnt[a]++;
        }
        f[0]=1;
        for(int i=1;i<=n;i++) 
        f[i]=f[i-1]*i%M;
        int s=n-k;
        for(int i=m;i;i--)
        {
            int sum=0;
            for(int j=i;j<=m;j+=i) 
            sum+=cnt[j];
            if(sum<s)
            {
                ans[i]=0; 
                continue; 
            }
            ans[i]=C(s,sum)*st(m/i-1,sum-s)%M*st(m/i,n-sum)%M;
            for(int j=i*2;j<=m;j+=i) 
            ans[i]=(ans[i]+M-ans[j])%M;
        }
        for(int i=1;i<m;i++) 
        printf("%lld ",ans[i]);
        printf("%lld",ans[m]);
        return 0;
    }
  • 相关阅读:
    csrf
    cookies和session区别
    关于Processing开发应用及发布分享的一些经验分享
    C++调用动态链接库DLL的隐式链接和显式链接基本方法小结
    C++最基本调用静态库的方法小结
    C++最基本调用动态链接库dll方法的小结
    基于Potplayer类播放器或Action!类录屏软件调取摄像头方式的定时抓拍保存图像方法小结
    基于Openframeworks调取摄像头方式的定时抓拍保存图像方法小结
    DOS使用技巧整理 [典型案例分享]
    正则表达式使用技巧整理
  • 原文地址:https://www.cnblogs.com/mgtnb/p/12329732.html
Copyright © 2011-2022 走看看