6月28日考试 题解(GCD约分+动态规划+树状数组二维偏序)

前言:考的一般般吧……T3暴力没打上来挺可惜的,到手的75分没了。

----------------------------------

T1 【JZOJ4745】看电影

Description

听说NOIP2016大家都考得不错,于是CCF奖励省常中了 K 张变形金刚5的电影票奖励OI队的同学去看电影。可是省常中OI队的同学们共有 N(N >= K)人。于是机智的你想到了一个公平公正的方法决定哪K人去看电影。
N个人排成一圈,按顺时针顺序标号为1 - N,每次随机一个还存活的人的编号,将这个人踢出。继续上述操作,直到剩下K个人。
但这样显然太无聊了,于是小S又想出一个牛逼的方法。
N个人排成一圈,按顺时针顺序标号为1 - N,每次随机一个1 - N的编号,假设随机到的编号是X,如果编号为X人还未踢出,则将这个人踢出,否则看编号为X % N + 1(即顺时针顺序下一个编号)的人是否存活,如果还未踢出则将他踢出,否则继续看编号(X + 1)% N +1的人,如果已被踢出看顺时针的下一个…………,以此类推,直到踢出一个人为止。重复上述操作,直到剩下K个人。
已知小S的编号是Id,问按照小S的方法来他有多少的概率可以不被踢出,成功得到看电影的机会。

Input

第一行包括三个正整数,N,K,Id(1<=K<=N<=10^9,1<=ID<=N )

Output

一行一个最简分数,表示小S可以看到电影的概率。
(如果概率为1或0,请输出1/1或0/1)

---------------------------

因为在环上每个人的位置和被选中的概率是相同的。所以所有人能看电影的概率和为K。即一个人看电影的概率是K/N。约分后输出即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,k,ld;
int gcd(int a,int b)
{
    if(b==0)return a;
    return gcd(b,a%b);
}
int main()
{
    scanf("%d%d%d",&n,&m,&ld);
    k=gcd(n,m);
    printf("%d/%d",m/k,n/k);
    return 0;
}

T2 【JZOJ4746】树塔狂想曲

相信大家都在长训班学过树塔问题,题目很简单求最大化一个三角形数塔从上往下走的路径和。走的规则是:(i,j)号点只能走向(i+1,j)或者(i+1,j+1)。如下图是一个数塔,映射到该数塔上行走的规则为:从左上角的点开始,向下走或向右下走直到最底层结束。
       1
       3 8
       2 5 0
       1 4 3 8
       1 4 2 5 0
路径最大和是1+8+5+4+4 = 22,1+8+5+3+5 = 22或者1+8+0+8+5 = 22。
小S觉得这个问题so easy。于是他提高了点难度,他每次ban掉一个点,然后询问你不走该点的最大路径和。

当然他上一个询问被ban掉的点过一个询问会恢复(即每次他在原图的基础上ban掉一个点,而不是永久化的修改)。

n<=1000 m<=50W

----------------------------------

看完题目后不难想到要维护最大值和次大值。我们不妨从上往下DP一次,从下往上DP一次,来维护$(i,j)$的最大值。

写法就是$tot[i][j]=\max(f[i-1][j-1],f[i-1][j])+\max(ff[i+1][j],ff[i+1][j+1])+a[i][j]$

最大值和次大值再遍历一遍就可以求出来了。

#include<bits/stdc++.h>
using namespace std;
int f[1005][1005],ff[1005][1005],sum[1005],ans[1005],tot[1005][1005],second[1005];
int n,m,a[1005][1005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
} 
void dp()
{
    f[1][1]=a[1][1];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=i;j++) f[i][j]=max(f[i-1][j-1],f[i-1][j])+a[i][j];
    for (int i=n;i>=1;i--)
        for (int j=i;j>=1;j--) ff[i][j]=max(ff[i+1][j],ff[i+1][j+1])+a[i][j];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=i;j++) tot[i][j]=max(f[i-1][j-1],f[i-1][j])+max(ff[i+1][j],ff[i+1][j+1])+a[i][j];
    for (int i=1;i<=n;i++)
        for (int j=1;j<=i;j++)
            if (tot[i][j]>ans[i]) ans[i]=tot[i][j],sum[i]=j;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=i;j++)
            if (j!=sum[i])
                if (tot[i][j]>second[i]) second[i]=tot[i][j];
}
int main()
{
    n=read(),m=read();
    for (int i=1;i<=n;i++)
        for (int j=1;j<=i;j++) a[i][j]=read();
    dp();
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        if (x==1&&y==1){
            cout<<-1<<endl;
            continue;
        }
        if (ans[x]==tot[x][y]) cout<<second[x]<<endl;
        else cout<<ans[x]<<endl;
    }
    return 0;
}

T3 【JZOJ4747】被粉碎的线段树

 题面

--------------------------------

我是没有看懂正解……贴一个正解连接吧:https://blog.csdn.net/dianning8393/article/details/101620538

不难想到75分的做法:同样使用线段树,只不过把每个区间的mid改为他给出的mid即可。区间权值设为1。注意要使用动态开点,如果使用2倍编号类型的线段树会挂25分,因为他给出的树不是完全二叉树。

貌似再加一些限制语句就可以到85分了,不过我没试过。

#include<bits/stdc++.h>
using namespace std;
struct node
{
    int l,r,sum,lc,rc;
}tree[4000005];
int n,m,a[1000005],cnt,cut[4000005],tot;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void build(int index,int l,int r)
{
    tree[index].l=l;tree[index].r=r;
    if (l==r){
        tree[index].sum=1;
        return;
    }
    int mid=a[++cnt];cut[index]=mid;
    if (!tree[index].lc) tree[index].lc=++tot,build(tree[index].lc,l,mid);
    if (!tree[index].rc) tree[index].rc=++tot,build(tree[index].rc,mid+1,r);
    tree[index].sum=1;
}
inline int query(int index,int l,int r)
{
    if (l<=tree[index].l&&tree[index].r<=r) return tree[index].sum;
    int mid=cut[index],res=0;
    if (l<=mid) res+=query(tree[index].lc,l,r);
    if (r>mid) res+=query(tree[index].rc,l,r);
    return res;
}
int main()
{
    n=read(),m=read();
    for (register int i=1;i<n;i++) a[i]=read();
    tot=1;
    build(1,1,n);
    for (register int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        printf("%d\n",query(1,x,y));
    }
    return 0;
}
posted @ 2020-06-28 12:25  我亦如此向往  阅读(190)  评论(0编辑  收藏  举报