• noip济南清北冲刺班DAY1


    上午

    T1 立方数

    题目描述

    LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数。

    现在给定一个数P,LYK想要知道这个数是不是立方数。

    当然你有可能随机输出一些莫名其妙的东西来骗分,因此LYK有T次询问~

    输入输出格式

    输入格式:

     

    第一行一个数T,表示有T组数据。

    接下来T行,每行一个数P。

     

    输出格式:

     

    输出T行,对于每个数如果是立方数,输出“YES”,否则输出“NO”。

     

    输入输出样例

    输入样例#1: 
    3
    8
    27
    28
    
    输出样例#1: 
    YES
    YES
    NO
    

    说明

    对于30%的数据p<=100。

    对于60%的数据p<=10^6。

    对于100%的数据p<=10^18,T<=100。

    题解:枚举10^6/二分。

    代码:二分0ms,枚举540ms

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    
    int t;
    LL x;
    
    LL read(){
        LL x=0,f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
        
    }
    
    bool check(LL x){
        for(int i=1;;i++){
            if(1LL*i*i*i==x)return true;
            if(1LL*i*i*i>x)return false;
        }
    }
    
    int main(){
        scanf("%d",&t);
        while(t--){
            scanf("%lld",&x);
            if(check(x))printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    枚举
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int main(){
        int t;
        scanf("%d",&t);
        for(int i=1;i<=t;i++){
            long long x;
            scanf("%lld",&x);
            long long l=1,r=1000000;
            bool flag=false;
            while(l<=r){
                int mid=(l+r)>>1;
                long long gg=1LL*mid*mid*mid;
                if(gg==x){
                    flag=true;break;//不break死循环 
                }
                else if(gg>x)r=mid-1;
                else l=mid+1;
            }
            if(flag)printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    二分

    T2立方数2

    题目描述

    LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数。

    LYK还定义了一个数叫“立方差数”,若一个数可以被写作是两个立方数的差,则这个数就是“立方差数”,例如7(8-1),26(27-1),19(27-8)都是立方差数。

    现在给定一个数P,LYK想要知道这个数是不是立方差数。

    当然你有可能随机输出一些莫名其妙的东西,因此LYK有T次询问~

    这个问题可能太难了…… 因此LYK规定P是个质数!

    输入输出格式

    输入格式:

     

    第一行一个数T,表示有T组数据。

    接下来T行,每行一个数P。

     

    输出格式:

     

    输出T行,对于每个数如果是立方差数,输出“YES”,否则输出“NO”。

     

    输入输出样例

    输入样例#1: 复制
    5
    2
    3
    5
    7
    11
    
    输出样例#1: 复制
    NO
    NO
    NO
    YES
    NO

    说明

    对于30%的数据p<=100。

    对于60%的数据p<=10^6。

    对于100%的数据p<=10^12,T<=100。

    题解:立方差公式

    设p=x^3-y^3

    =(x-y)*(x^2+xy+y^2)

    因为p是个素数,所以x-y=1,x^2+xy+y^2=p。

    所以x=y+1,代入x^2+xy+y^2得,

    (y+1)^2+(y+1)*y+y^2=p

    3*y^2+3y+1=p

    所以10^3枚举y就好了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define LL long long
    using namespace std;
    
    int t;
    
    bool check(LL x){
        for(int i=1;;i++){
            LL tmp=3LL*i*i+3*i+1;
            if(tmp==x)return true;
            if(tmp>x)return false;
        }
    }
    
    int main(){
        scanf("%d",&t);
        while(t--){
            LL p;
            scanf("%lld",&p);
            if(check(p))printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    AC

    T3 猜数字

    题目描述 

    LYK在玩猜数字游戏。

    总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。

    我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!

    例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。

    你需要告诉LYK,它第几次猜数字开始就已经矛盾了。

    输入输出格式

    输入格式:

    第一行两个数n和T,表示有n个数字,LYK猜了T次。

    接下来T行,每行三个数分别表示li,ri和xi。

    输出格式:

    输出一个数表示第几次开始出现矛盾,如果一直没出现矛盾输出T+1。

    输入输出样例

    输入样例#1: 复制
    20 4
    1 10 7
    5 19 7
    3 12 8
    1 20 1
    
    输出样例#1: 复制
    3

    说明

    对于50%的数据n<=8,T<=10。

    对于80%的数据n<=1000,T<=1000。

    对于100%的数据1<=n,T<=1000000,1<=li<=ri<=n,1<=xi<=n(但并不保证一开始的所有数都是1~n的)。

    题解:

    线段树骗分20

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define maxn 10000
    #define inf 10000000
    using namespace std;
    
    int n,t;
    struct Tree{
        int l,r,mn,mx,s;
    }tr[maxn<<2];
    
    inline int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
        for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
        return x * f;
    }
    
    void pushup(int rt){
        tr[rt].mn=min(tr[rt<<1].mn,tr[rt<<1|1].mn);
        tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx);
        return ;
    }
    
    void pushdown(int rt){
        if(tr[rt].s==0)return;
        tr[rt<<1].mn=tr[rt<<1].mx=tr[rt].s;
        tr[rt<<1|1].mn=tr[rt<<1|1].mx=tr[rt].s;
        tr[rt].s=0;
    }
    
    void build(int rt,int l,int r){
        tr[rt].l=l;tr[rt].r=r;
        if(l==r){
            tr[rt].mn=tr[rt].mx=inf;
            return;
        }
        int mid=(l+r)>>1;
        build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
        pushup(rt);
    }
    
    void modify(int rt,int l,int r,int ql,int qr,int c){
        if(l>=ql&&r<=qr){
            tr[rt].mn=tr[rt].mx=c;
            tr[rt].s=c;
            return;
        }
        int mid=(l+r)>>1;
        if(ql<=mid)modify(rt<<1,l,mid,ql,qr,c);
        if(qr>mid)modify(rt<<1|1,mid+1,r,ql,qr,c);
        pushup(rt);
    }
    
    int query_mn(int rt,int l,int r,int ql,int qr){
        if(l>=ql&&r<=qr){
            return tr[rt].mn;
        }
        pushdown(rt);
        int mid=(l+r)>>1,ans=inf;
        if(ql<=mid)ans=min(ans,query_mn(rt<<1,l,mid,ql,qr));
        if(qr>mid)ans=min(ans,query_mn(rt<<1|1,mid+1,r,ql,qr));
        return ans;
    }
    
    int query_mx(int rt,int l,int r,int ql,int qr){
        if(l>=ql&&r<=qr){
            return tr[rt].mx;
        }
        pushdown(rt);
        int mid=(l+r)>>1,ans=-inf;
        if(ql<=mid)ans=max(ans,query_mx(rt<<1,l,mid,ql,qr));
        if(qr>mid)ans=max(ans,query_mx(rt<<1|1,mid+1,r,ql,qr));
        return ans;
    }
    
    int main(){
        n=read();t=read();
        if(n>maxn){
            printf("%d
    ",t+1);
            fclose(stdin);fclose(stdout);
            return 0;
        }
        build(1,1,n);
        int l,r,x;
        for(int i=1;i<=t;i++){
            int l,r,x;
            l=read();r=read();x=read();
            int mn=query_mn(1,1,n,l,r);
            int mx=query_mx(1,1,n,l,r);
            if(x>mn||(x<mn&&mx!=inf)){
                printf("%d
    ",i);
                fclose(stdin);fclose(stdout);
                return 0;
            }
            modify(1,1,n,l,r,x);
        }
        printf("%d
    ",t+1);
        return 0;
    }
    20

    判定性问题

    题目大意:给定一堆询问,问出现矛盾的询问是哪一条。

    方法:

    1)枚举+判断

    2)二分

    l=1,r=q

    如果在询问在mid时候已经出现了矛盾,出现矛盾的地方一定在mid左边。

    那么现在的问题已经缩小在了mid左边。

    具体做法是将1--mid的询问按照min值从大到小排序。依次覆盖区间,

    那么不合法的情况有两个:

    a、两个区间min值相同,但是不相交。因为数都是不同的。

    b、枚举的当前区间已经被之前的区间覆盖了。

    那么只需要维护一下这个区间是否被覆盖了。

    线段树是会T的。用并查集维护。fa[i]表示从i开始右边第一个没有被区间覆盖的点。

    在处理注意:对于最小值相同的,我们要求区间交和区间并,那么最小值一定在

    区间并里。只要判断当前区间有没有之前区间覆盖。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 1000009
    using namespace std;
    
    int n,m,l,r,mid,ans,lmn,lmx,rmn,rmx;
    int fa[maxn];
    
    inline int read(){
        int x=0,f=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
        for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
        return x*f;
    }
    
    struct Q{
        int l,r,mn;
    }q[maxn],t[maxn];
    
    bool cmp(Q a,Q b){
        return a.mn>b.mn;
    }
    
    int f(int x){
        return fa[x]==x?x:fa[x]=f(fa[x]);
    }
    
    bool check(int k){
        for(int i=1;i<=n+1;i++)fa[i]=i;
        for(int i=1;i<=k;i++)t[i]=q[i];
        sort(t+1,t+k+1,cmp);
        lmn=lmx=t[1].l;rmn=rmx=t[1].r;
        for(int i=2;i<=k;i++){
            if(t[i].mn<t[i-1].mn){
                if(f(lmx)>rmn)return true;
                for(int j=f(lmn);j<=rmx;j=f(j+1))
                 fa[f(j)]=f(rmx+1);
                lmn=lmx=t[i].l;rmn=rmx=t[i].r;
            }else{
                lmn=min(lmn,t[i].l);lmx=max(lmx,t[i].l);
                rmn=min(rmn,t[i].r);rmx=max(rmx,t[i].r);
                if(lmx>rmn)return true;
            }
        }
        if(f(lmx)>rmn)return true;
        return false;
    }
    
    int main(){
        n=read();m=read();
        for(int i=1;i<=m;i++)q[i].l=read(),q[i].r=read(),q[i].mn=read();
        l=1;r=m;ans=m+1;
        while(l<=r){
            mid=(l+r)>>1;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
        printf("%d
    ",ans);
        return 0;
    }
    AC

    下午

    T1 水题

    题目描述

    LYK出了道水题。

    这个水题是这样的:有两副牌,每副牌都有n张。

    对于第一副牌的每张牌长和宽分别是xi和yi。对于第二副牌的每张牌长和宽

    分别是aj和bj。第一副牌的第i张牌能覆盖第二副牌的第j张牌当且仅当xi>=aj

    并且yi>=bj。(注意牌不能翻转)当然一张牌只能去覆盖最多一张牌,而不

    能覆盖好多张。LYK想让两副牌的各n张一一对应叠起来。它想知道第二副

    牌最多有几张能被第一副牌所覆盖。

    输入输出格式

    输入格式:

     

    第一行一个数n。

    接下来n行,每行两个数xi,yi。

    接下来n行,每行两个数aj,bj。

     

    输出格式:

     

    输出一个数表示答案。

     

    输入输出样例

    输入样例#1: 复制
    3
    2 3
    5 7
    6 8
    4 1
    2 5
    3 4
    
    输出样例#1: 复制
     
    2



    题解:

    错误贪心
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define maxn 10009
    using namespace std;
    
    int n,ans,vis[maxn];
    
    struct ZFX{
        int x,y;
    }a[maxn],b[maxn];
    
    bool cmp(ZFX a,ZFX b){
        return a.x<b.x;
    }
    
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
        for(int i=1;i<=n;i++)scanf("%d%d",&b[i].x,&b[i].y);
        sort(a+1,a+n+1,cmp);sort(b+1,b+n+1,cmp);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(vis[j])continue;
                if(a[i].x>=b[j].x&&a[i].y>=b[j].y){
                    vis[j]=true;ans++;break;
                }
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    10
    
    

    正解:对于ai,我们把长度小于ai的b类牌放入一个集合,找到

    高度最小的进行覆盖。用个数据结构维护一下....

    stl set知道吧....set是没有重复元素,multiset是有重复元素。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<set>
    #define maxn 100009
    using namespace std;
    multiset<int>q;
    int n,k,ans;
    
    struct T{
        int x,y;
    }a[maxn],b[maxn];
    
    bool cmp(T a,T b){
        return a.x<b.x;
    }
    
    int main(){
        scanf("%d",&n);k=1;
        for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y);
        for(int i=1;i<=n;i++)scanf("%d%d",&b[i].x,&b[i].y);
        sort(a+1,a+n+1,cmp);sort(b+1,b+n+1,cmp);
        q.clear();
        for(int i=1;i<=n;i++){
            while(b[k].x<=a[i].x&&k<=n){
                q.insert(b[k].y);k++;
            }
            if(q.empty())continue;
            multiset<int>::iterator it=q.upper_bound(a[i].y);
            if(it==q.begin())continue;
            ans++;it--;q.erase(it);
        }
        cout<<ans<<endl;
    }
    AC

    T2 梦境

    题目描述

    LYK做了一个梦。

    这个梦是这样的,LYK是一个财主,有一个仆人在为LYK打工。

    不幸的是,又到了月末,到了给仆人发工资的时间。但这个仆人很奇怪,它可能想要至少x块钱,并且当LYK凑不出恰好x块钱时,它不会找零钱给LYK。

    LYK知道这个x一定是1~n之间的正整数。当然抠门的LYK只想付给它的仆人恰好x块钱。但LYK只有若干的金币,每个金币都价值一定数量的钱(注意任意两枚金币所代表的钱一定是不同的,且这个钱的个数一定是正整数)。LYK想带最少的金币,使得对于任意x,都能恰好拼出这么多钱。并且LYK想知道有多少携带金币的方案总数。

    具体可以看样例。

    输入输出格式

    输入格式:

     

    第一行一个数n,如题意所示。

     

    输出格式:

     

    输出两个数,第一个数表示LYK至少携带的金币个数,第二数表示方案总数。

     

    输入输出样例

    输入样例#1: 复制
    6
    输出样例#1: 复制
    3 2
    输入样例#2: 复制
    10
    输出样例#2: 复制
    4 8

    说明

    LYK需要至少带3枚金币,有两种方案,分别是{1,2,3},{1,2,4}来恰好得到任意的1~n之间的x。

    数据范围

    对于30%的数据n<=10。

    对于60%的数据n<=100。

    对于100%的数据n<=1000。

    题解:

    dp求最少金币 dfs求方案数 dp是n^2的

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define maxn 100009
    using namespace std;
    
    int n,ans,gans,cnt;
    int dp[maxn],tmp[maxn],f[maxn],use[maxn];
    
    void slove(int x){
        if(!dp[x]){
            ans++;
            for(int i=n;i>=1;i--){
                dp[i]+=dp[i-x];
            }
        }
    }
    
    void dfs(int x,int nxt){
        if(x==ans+1){
            int ok=0;
            memset(f,0,sizeof(f));f[0]=1;
            for(int j=1;j<=ans;j++){
                for(int i=n;i>=1;i--)
                  f[i]+=f[i-tmp[j]];
            }
            for(int i=1;i<=n;i++) if(!f[i])return;
            cnt++;
            return;
        }
        for(int i=nxt;i<=n;i++){
            if(use[i])continue;
            tmp[x]=i;use[i]=1;
            dfs(x+1,i+1);
            use[i]=0;
        }
    }
    
    int main(){
    
    //    freopen("biao.out","w",stdout);
           scanf("%d",&n);
           dp[0]=1;
           for(register int i=1;i<=n;i++)slove(i);
           dfs(1,1);
           printf("%d %d
    ",ans,cnt);
        return 0;
    }
    30

    正解:

    如果你是财主肯定拿 1 2 4 8 16....

    那么最少的金币数量就是log2(n)向下取整+1

    那么第二问可以dp或者dfs,因为金币数量最大好像是10....

    dp是这样的..

    dp[i][j][k]拿了i个金币,和为j,最大值为k的方案数,那么下一次

    拿金币的范围是[k+1,j+1].

    那么转移为

    dp[i+1][j+h][h]+=dp[i][j][k].

    第一维可以用滚动掉...不写了。不会

    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    
    int n;
    int dp[20][1024][1024];
    
    int main(){
        scanf("%d",&n);
        int p=(int)(log(n)/log(2)+0.000000001)+1;
        int ans=0;
        dp[1][1][1]=1;//拿个i个数,和为j,最大k
        for(int a=1;a<=p;a++){
            for(int b=1;b<=n;b++){
                for(int c=1;c<=n;c++){
                    if(dp[a][b][c]){
                        for(int d=c+1;d<=b+1;d++){
                            dp[a+1][min(b+d,n)][d]+=dp[a][b][c];
                        }
                    }
                }
            }
        }
        for(int i=1;i<=n;i++)ans+=dp[p][n][i];
        cout<<p<<" "<<ans<<endl;
        return 0;
    }
    AC


    T3动态规划

    题目描述

    
    

    LYK在学习dp,有一天它看到了一道关于dp的题目。

    这个题目是这个样子的:一开始有n个数,一段区间的价值为这段区间相同的数的对数。

    我们想把这n个数切成恰好k段区间。之后这n个数的价值为这k段区间的价值和。我们想

    让最终这n个数的价值和尽可能少。例如6个数1,1,2,2,3,3要切成3段,一个好方法是切成

    [1],[1,2],[2,3,3],这样只有第三个区间有1的价值。因此这6个数的价值为1。

    LYK并不会做,丢给了你。

    输入输出格式

    输入格式:

    第一行两个数n,k。

    接下来一行n个数ai表示这n个数。

    输出格式:

    一个数表示答案。

    输入输出样例

    
    
    输入样例#1: 复制
    10 2
    1 2 1 2 1 2 1 2 1 2
    
    输出样例#1: 复制
    8
    
    

    说明

    
    

    对于30%的数据n<=10。

    
    

    对于60%的数据n<=1000。

    
    

    对于100%的数据1<=n<=100000,1<=k<=min(n,20),1<=ai<=n。

    
    

    其中有30%的数据满足ai完全相同均匀分布在所有数据中。


    题解:
    对于60%的数据,n^2预处理区间价值,
    dp[i][j]表示前i个数分了j段,转移为
    dp[i][j]=min{dp[k][j-1]+sum[k+1][j]}
    正解:分治优化dp 弃疗。
    代码:
    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int N=100010;
    typedef long long LL;
    int c[N],a[N];
    LL f[N],g[N];
    int p,q,n,k;
    LL tot;
    void move(int l,int r)  // [p,q]之前的区间
    {
        while (l<p) p--,tot+=c[a[p]],c[a[p]]++;
        while (r>q) q++,tot+=c[a[q]],c[a[q]]++;
        while (p<l) c[a[p]]--,tot-=c[a[p]],p++;
        while (r<q) c[a[q]]--,tot-=c[a[q]],q--;
    }
    void work(int l,int r,int fl,int fr)
    //需要求dp[fl] ~ dp[fr]  最优解一定从l~r中的某一个转移过来
    {
        if (fl>fr) return;
        int mid=(fl+fr)>>1,mi;
        LL mx=1LL<<60;
        for (int i=l;i<=r;i++)
        if (i<mid)
        {
            move(i+1,mid); -> tot=sum(i+1,mid);
            if (f[i]+tot<mx) mx=f[i]+tot,mi=i;
        }
        g[mid]=mx;
        work(l,mi,fl,mid-1);
        work(mi,r,mid+1,fr);
    }
    int main()
    {
        freopen("dp.in","r",stdin);
        freopen("dp.out","w",stdout);
        scanf("%d%d",&n,&k);
        for (int i=1;i<=n;i++) scanf("%d",&a[i]);
        f[0]=0;
        for (int i=1;i<=n;i++) f[i]=1LL<<60;
        while (k--)
        {
            p=1,q=0,tot=0;
            for (int i=1;i<=n;i++) c[i]=0;
            work(0,n-1,1,n);
            for (int i=0;i<=n;i++) f[i]=g[i],g[i]=0;
        }
        cout<<f[n];
        return 0;
    }
    std
    
    

    反思:
    因为改变读入优化的风格..然后前两个题读不进去数....
    打表打半天...结果发现不知什么时候把窗口关了..什么都没打出来..
    重新打...十五分钟出来一点点....时间紧张最后很匆忙...
    一定把暴力打完..不打玄学暴力...orz
  • 相关阅读:
    mysql 高效分页控件及c#调用实例
    sql server高效分页控件及c#调用实例
    Log4net从下载到使用例子
    Nlog从下载到使用例子
    使用httpClient调用接口,参数用map封装或者使用JSON参数,并转换返回结果
    Pool thread stack traces: Thread[C3P0PooledConnectionPoolManager[identityToken->原因解决办法
    使用spring的aop监听所有controller或者action日志
    ActiveMQ监听消息并进行转发,监听不同的mq服务器和不同的队列
    zookeeper集群查看状态时报错Error contacting service. It is probably not running的一些坑以及解决办法
    AopProxyUtils.getSingletonTarget(Ljava/lang/Object;)Ljava/lang/Object;大坑
  • 原文地址:https://www.cnblogs.com/zzyh/p/7747456.html
Copyright © 2020-2023  润新知