7.1集训模拟赛5(......)

A. 最大公约数和最小公倍数问题:

题目描述:

输入二个正整数x0,y0(2<=x0<100000,2<=y0<=1000000),求出满足下列条件的P,Q的个数

条件:

  1. P,Q是正整数
  2. 要求P,Q以x0为最大公约数,以y0为最小公倍数.

试求:满足条件的所有可能的两个正整数的个数.

输入格式:

一行,二个正整数x0,y0。

输出格式:

一行,即满足条件的所有可能的两个正整数的个数。保证答案不需要高精度。

样例

样例输入:

3 60

样例输出:

 4

 关于求gcd和lcm

#include<bits/stdc++.h>
using namespace std;
int x,y;
int ans;
int gcd(int a,int b){//求最大公约数
    //if(a<b)swap(a,b);
    if(a%b==0)return b;
    if(a==b)return b;
    return gcd(b,a%b);
}

int lcm(int a,int b){//求最小共倍数
    return a*b/gcd(a,b);
}

int main(){
    scanf("%d%d",&x,&y);
    for(int i=x;i<=y;i++){
        int j=x*y/i;
        if(gcd(i,j)==x&&lcm(i,j)==y){
            ans++;
        }
    }
    printf("%d",ans);
    return 0;
}

 

B. 方格取数:

题目描述:

设有N*N的方格图(N<=20,我们将其中的某些方格中填入正整数,而其他的方格中则放入数字0。如下图所示(见样例):

某人从图的左上角的A(1,1) 点出发,可以向下行走,也可以向右走,直到到达右下角的B(n,n)点。在走过的路上(包括起点在内),他可以取走方格中的数(取走后的方格中将变为数字0)。此人从A点到B 点共走两次,试找出2条这样的路径,使得取得的数之和为最大。

输入格式:

输入的第一行为一个整数N(表示N*N的方格图)

接下来的每行有三个整数,前两个表示位置,第三个数为该位置上所放的数。一行单独的0表示输入结束。

输出格式:

只需输出一个整数,表示2条路径上取得的最大的和。

样例:

样例输入:

8
2 3 13
2 6 6
3 5 7
4 4 14
5 2 21
5 6 4
6 3 15
7 2 14
0 0 0

样例输出:

67
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int f[N][N][N][N],a[N][N];
int n,x,y,w;
int main(){
    scanf("%d",&n);
    while(scanf("%d%d%d",&x,&y,&w)){
        if(x==0)break;
        a[x][y]=w;
    }
    f[1][1][1][1]=a[1][1];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){//i,j表示第一个人或第一条路径走到i,j的最大分数
            for(int k=1;k<=n;k++){
                for(int l=1;l<=n;l++){//同i,j
                    if(i==k&&j==l){
                        f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i][j-1][k-1][l]),max(f[i][j-1][k][l-1],f[i-1][j][k][l-1]))+a[k][l];
                    } else {
                        f[i][j][k][l]=max(max(f[i-1][j][k-1][l],f[i][j-1][k-1][l]),max(f[i][j-1][k][l-1],f[i-1][j][k][l-1]))+a[i][j]+a[k][l];
                    }
                }
            }
        }
    }
    printf("%d",f[n][n][n][n]);
    return 0;
}

在来个三维的Code:

#include <bits/stdc++.h>
using namespace std;
int n;
int pan[25][25];
int dp[25][25][25][25];
int main(){
    scanf("%d",&n);
    int x,y,w;
    while(scanf("%d%d%d",&x,&y,&w)==3){
        if(x==0&&y==0&&w==0)break;
        pan[x][y]=w;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int l=1;l<=n;l++){
                int r=i+j-l;
                if(i==l&&j==r){
                    dp[i][j][l][r]=pan[i][j]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
                }else{
                    dp[i][j][l][r]=pan[i][j]+pan[l][r]+max(max(dp[i-1][j][l-1][r],dp[i-1][j][l][r-1]),max(dp[i][j-1][l-1][r],dp[i][j-1][l][r-1]));
                }
            }
        }
    }
    cout<<dp[n][n][n][n]<<endl;
    return 0;
}

C. WYT的刷子:

题目描述:

WYT有一把巨大的刷子,刷子的宽度为M米,现在WYT要使用这把大刷子去粉刷有N列的栅栏(每列宽度都为1米;每列的高度单位也为米,由输入数据给出)。

使用刷子的规则是:

  1. 与地面垂直,从栅栏的底部向上刷
  2. 每次刷的宽度为M米(当剩余栅栏宽度不够M米的话,刷子也可以使用,具体看样例2)
  3. 对于连续的M列栅栏,刷子从底向上,刷到的高度只能到这M列栅栏的最低高度。

WYT请你回答两个问题:

  1. 最少有多少个单位面积不能刷到(单位面积为1平米)
  2. 在满足第一问的条件下,最少刷几次?

输入格式:

共两行:

第一行两个整数N和M。

第二行共N个整数,表示N列栅栏的高度。

输出格式:

一行,两个整数,分别为最少剩余的单位面积数量和最少刷的次数。

样例

样例输入1:

5 3
5 3 4 4 5

样例输出1:

3
2

样例输入2:

10 3
3 3 3 3 3 3 3 3 3 3

样例输出2:

0
4

样例输入3:

7 4
1 2 3 4 3 2 1

样例输出3:

4
4

样例1的解释:

高度分别为 5 3 4 4 5 如上:

黄色的方块表示共有3个单位面积没刷上

绿色的框和红色的框表示一共刷了两次。

数据范围与提示:

30%的数据:N<=10^3

50%的数据:N<=10^5

100%的数据:1<=N<=10^6, 1<=M<=10^6,N>=M, 每列栅栏的高度<=10^6.

分析:

这到题需要用一个单调队列维护向左向右同高度能延伸的宽度。将向左向右的相加减一就是这一高度(所在平面)的宽度。

我们用maxh数组记录坐标为i可以刷的做大高度。

因为要求最少剩下的方块数量,下一步我们就可以求一下每个栅栏还剩多少不能刷,递推处理一下每个栅栏能被刷的最大高度。
我们先从左往右考虑一下,如果第i个栅栏能被刷完,那么最大高度就是它本身的高度;如果不能,则它的高度一定比它左边的栅栏能刷的最大高度要大(如果不成立,那么它完全可以接在它左边的栅栏上,把自己刷完),因此它能刷到的最大高度就是它左边的栅栏能刷到的最大高度。
然后还要从右边处理一下,有可能对于每一个栅栏,右边过来可能刷的更高,所以需要求一下最值。
既然有了每个栅栏能刷的最大高度,那么用每个栅栏的高度之和减去所有能刷的最大高度就是剩余的面积。

从左向右,从右向左更新maxh的值,同时记录最少没有刷的,最终输出。

小结:

这道题我是又找教练员认认真真又听了一边,对于单调队列得维护可以模拟一下(模拟了之后非常理解),记录延伸宽度也是模拟了,也非常理解。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
typedef long long ll;
ll n,m;
ll a[N];
ll q[N];
ll j[N];
ll maxh[N],l[N],r[N];
ll sum;
int main(){
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        sum+=a[i];//记录总面积
    }
    int head=0,tail=-1;
    for(int i=1;i<=n+1;i++){
        while(head<=tail&&a[i]<a[q[tail]]){
            r[q[tail]]=i-q[tail];//单调队列维护求向右延伸的宽度
            tail--;
        }
        q[++tail]=i;
    }
    head=0;tail=-1;
    for(int i=n;i>=0;i--){
        while(head<=tail&&a[i]<a[q[tail]]){
            l[q[tail]]=q[tail]-i;//维护向左延伸的宽度
            tail--;
        }
        q[++tail]=i;
    }
    for(int i=1;i<=n;i++){
        if(l[i]+r[i]-1>=m)j[i]=1;//j是一个记录数组,作用是记录第i个柱子能否被刷完,用bool类型的也可以
    }//这就用到l数组和r数组了注意减一
    for(int i=1;i<=n;i++){//从左向右
        if(j[i]==1){
            maxh[i]=a[i];//如果i刷不完,它一定得与i-1能刷的最大高度
        } else {
            maxh[i]=maxh[i-1];
        }
    }
    for(int i=n;i>=1;i--){//从右向左
        if(!j[i]){//如果i刷不完
            maxh[i]=max(maxh[i],maxh[i+1]);//那么i能刷的最大高度为i和i+1的最大高度
        }
        sum-=maxh[i];
    }
    ll k=2,ans=0;
    while(k<=n+1){//找同一高度的宽度
        int cnt=1;
        while(k<=n+1&&maxh[k]==maxh[k-1]){
            cnt++;
            k++;
        }
        ans+=cnt/m;
        if(cnt%m!=0)ans++;//如果宽度不能整除,肯定还要在加一次
        k++;
    }
    printf("%lld\n%lld",sum,ans);
    return 0;
}

D. 2017种树:

题目描述:

2017共有N棵树从0到N-1标号。现要把这些树种在一条直线上,第i棵树的种植位置X[i]如下确定:

X[0] = X[0] MOD L;

X[i] = (X[i-1]*A+B) MOD L。

每棵树种植的费用,是所有标号比它小的树与它的距离之和。2017请你计算各棵树的费用之积,最后对1000000007取余。

输入格式:

共五行:

第一行为N

第二行为L

第三行为X[0]

第四行为A

第五行为B

输出格式:

总费用

样例:

样例输入:

5
10
3
1
1

样例输出:

180

样例解释:

5棵树的位置分别为: 3, 4, 5, 6, 7.

费用分别为: 1, 3, 6, 10. (从第一棵树开始)

总费用为: 1 × 3 × 6 × 10 = 180.

数据范围与提示:

10%的数据:N<=10;

60%的数据:N<=2×10^5;

100%的数据:N,L<=200000; X[0] ,A, B<=10^9.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010;
ll n,l,x,a,b;
ll c1,c2,s1,s2;
ll cost,ans = 1;
struct edge{
    ll cnt;
    ll val;
}tree[N<<2];



void updata(int rt,int l,int r,int x,int val){
    if (l > x || r < x) return;
    if(l==r){
        tree[rt].val+=val;
        tree[rt].cnt++;
        return ;
    }
    ll mid = (l+r)>>1;
    if(x<=mid){
        updata(rt*2,l,mid,x,val);
    } else {
        updata(rt*2+1,mid+1,r,x,val);
    }
    tree[rt].cnt=tree[rt*2].cnt+tree[rt*2+1].cnt;
    tree[rt].val=tree[rt*2].val+tree[rt*2+1].val;
    //return ;
}

ll getval(int rt,int l,int r,int x,int y){
    if(y<l||x>r)return 0;
    if(x<=l&&y>=r)return tree[rt].val;
    ll mid = (l+r)>>1;
    ll sum=0;
    if(x<=mid){
        sum+=getval(rt*2,l,mid,x,y);
    }if(y>mid){
        sum+=getval(rt*2+1,mid+1,r,x,y);
    }
    return sum;
}

ll getcnt(int rt,int l,int r,int x,int y){
    if(y<l||x>r)return 0;
    if(x<=l&&y>=r)return tree[rt].cnt;
    ll mid = (l+r)>>1;
    ll sum=0;
    if(x<=mid){
        sum+=getcnt(rt*2,l,mid,x,y);
    }if(y>mid){
        sum+=getcnt(rt*2+1,mid+1,r,x,y);
    }
    return sum;
}


int main(){
    scanf("%lld%lld%lld%lld%lld",&n,&l,&x,&a,&b);
    x=x%l;
    //updata(1,1,l,x+1,x);
    for(int i=1;i<n;i++){
        updata(1,1,l,x+1,x+1);
        x=(x*a+b)%l;
        c1=getcnt(1,1,l,1,x+1);
        c2=i-c1;
        s1=getval(1,1,l,1,x+1);
        s2=tree[1].val-s1;
        cost = (c1*(x+1)-s1+s2-c2*(x+1))%1000000007;
        //printf("%lld  %lld  %lld  %lld\n",c1,c2,s1,s2);
        //printf("%lld\n",cost);
        ans = (ans*cost)%1000000007;
        //updata(1,1,l,x+1,x);
    }
    printf("%lld",ans);
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
typedef long long ll;
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define MAXTREE 200001

const int N = 200000 + 10;
const int MOD = 1000000007;

struct SegTree {
    ll sum;
    int cnt;
} tree[N << 2];

int n, L, a, b;
int x;
ll ans = 1;
int tot;

void pushup(int rt) {
tree[rt].sum = (tree[lson].sum + tree[rson].sum);
tree[rt].cnt = tree[lson].cnt + tree[rson].cnt;
}

void update(int rt, int L, int R, int pos, int val) {
    if (L > pos || R < pos) return;
    if (L == R) {
        tree[rt].sum = (tree[rt].sum + val);
        ++tree[rt].cnt;
        return;
    }
    int mid = (L + R) >> 1;
    if (mid < pos) update(rson, mid+1, R, pos, val);
    else update(lson, L, mid, pos, val);
    pushup(rt);
}

int qeurycnt(int rt, int L, int R, int l, int r) {
    if (L > r || R < l) return 0;
    if (l <= L && r >= R) return tree[rt].cnt;
    int mid = (L + R) >> 1;
    return qeurycnt(lson, L, mid, l, r) + qeurycnt(rson, mid + 1, R, l, r);
}

ll qeurysum(int rt, int L, int R, int l, int r) {
    if (L > r || R < l) return 0;
    if (l <= L && r >= R) return tree[rt].sum;
    int mid = (L + R) >> 1;
    return (qeurysum(lson, L, mid, l, r) + qeurysum(rson, mid + 1, R, l, r));
}

int main() {
    scanf("%d%d%d%d%d", &n, &L, &x, &a, &b);
    x = x % L;
    int c1, c2;
    ll s1, s2, sum = 0;
    for (int i = 1; i < n; ++i) {
    update(1, 1, L, x+1, x+1); // 从 1 开始
    //sum = (sum + x + 1) % MOD;
    x = ((ll)x * a % L + b) % L;
    //printf("x = %lld\n", x);

    c1 = qeurycnt(1, 1, L, 1, x+1);
    //c2 = qeurycnt(1, 1, MAXTREE, x+1, MAXTREE);
    c2 = i - c1;
    s1 = qeurysum(1, 1, L, 1, x+1);
    //s2 = qeurysum(1, 1, MAXTREE, x+1, MAXTREE);
    s2 = tree[1].sum - s1;
    printf("%d  %d  %lld  %lld\n",c1,c2,s1,s2);
    //printf("c1 = %d c2 = %d s1 = %lld s2 = %lld\n", c1, c2, s1, s2);

    ll cost = (((ll)c1 * (x+1) % MOD - s1 + s2 - (ll)c2 * (x+1) %MOD)%MOD+MOD)%MOD;
    //printf("cost = %lld\n", cost);
    ans = ans * cost % MOD;
    
    //update(1, 1, MAXTREE, x, x);
}
printf("%lld\n", ans);
return 0;
}

 分析:

 

 

 

 
posted @ 2020-07-02 20:23  LightyaChoo  阅读(198)  评论(0编辑  收藏  举报