11.01 早上 枚举+二分+并查集

12 篇文章 0 订阅
11 篇文章 0 订阅

立方数(cubic)

Time Limit:1000ms Memory Limit:128MB

题目描述

LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数。
现在给定一个数P,LYK想要知道这个数是不是立方数。
当然你有可能随机输出一些莫名其妙的东西来骗分,因此LYK有T次询问~

输入格式(cubic.in)
第一行一个数T,表示有T组数据。
接下来T行,每行一个数P。

输出格式(cubic.out)
输出T行,对于每个数如果是立方数,输出“YES”,否则输出“NO”。

输入样例
3
8
27
28

输出样例
YES
YES
NO

数据范围
对于30%的数据p<=100。
对于60%的数据p<=10^6。
对于100%的数据p<=10^18,T<=100。

题解

p<=10^18,p=x^3
x<=10^6
暴力枚举。
甚至都不用排序,O(Tx)都能过

代码

#include<iostream>
#include<cstdio>

#include<algorithm>
using namespace std;
int t,ans[250];
struct node{
    int id;
    long long num;
}tmp[251];
bool cmp(node a,node b){
    return a.num<b.num;
}
int main(){
    freopen("cubic.in","r",stdin);
    freopen("cubic.out","w",stdout);

    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        scanf("%lld",&tmp[i].num);
        tmp[i].id=i;
    }
    sort(tmp+1,tmp+t+1,cmp);
    int cnt=1;
    for(long long i=1;;i++){
        long long p=i*i*i;
        while(cnt<=t&&p>tmp[cnt].num){
            cnt++;
        }
        if(cnt>t) break;
        if(p==tmp[cnt].num){
            ans[tmp[cnt].id]=1;
            cnt++;
            if(cnt>t) break;
            while(cnt<=t&&tmp[cnt].num==tmp[cnt-1].num){
                ans[tmp[cnt].id]=1;
                cnt++;
            }
        }

    }
    for(int i=1;i<=t;i++){
        if(ans[i]){
            printf("YES\n");
        }else printf("NO\n");
    }
}

立方数2(cubicp)

Time Limit:1000ms Memory Limit:128MB

题目描述

LYK定义了一个数叫“立方数”,若一个数可以被写作是一个正整数的3次方,则这个数就是立方数,例如1,8,27就是最小的3个立方数。
LYK还定义了一个数叫“立方差数”,若一个数可以被写作是两个立方数的差,则这个数就是“立方差数”,例如7(8-1),26(27-1),19(27-8)都是立方差数。
现在给定一个数P,LYK想要知道这个数是不是立方差数。
当然你有可能随机输出一些莫名其妙的东西,因此LYK有T次询问~
这个问题可能太难了…… 因此LYK规定P是个质数!

输入格式(cubicp.in)
第一行一个数T,表示有T组数据。
接下来T行,每行一个数P。

输出格式(cubicp.out)
输出T行,对于每个数如果是立方差数,输出“YES”,否则输出“NO”。

输入样例
5
2
3
5
7
11

输出样例
NO
NO
NO
YES
NO

数据范围
对于30%的数据p<=100。
对于60%的数据p<=10^6。
对于100%的数据p<=10^12,T<=100。

题解

初中数学专场:p=a^3-b^3=(a-b)(a^2+a*b+b^2) a>=1,b>=1

因为p是质数,因子只有1和它本身,显然(a^2+a*b+b^2)!=1,那么a-b=1;
p<10^12 a<10^6

暴力枚举a。

O(Tx)也能过

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
int t,ans[251];
struct node{
    int id;
    long long num;
}tmp[251];
bool cmp(node a,node b){
    return a.num<b.num;
}
int main(){
    freopen("cubicp.in","r",stdin);
    freopen("cubicp.out","w",stdout);
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        scanf("%lld",&tmp[i].num);
        tmp[i].id=i;
    }
    sort(tmp+1,tmp+t+1,cmp);
    int cnt=1;
    for(long long i=1;;i++){
        long long j=i+1;
        long long p=i*i+j*j+j*i;
        while(cnt<=t&&p>tmp[cnt].num){
            cnt++;
        }
        if(cnt>t) break;
        if(p==tmp[cnt].num){
            ans[tmp[cnt].id]=1;
            cnt++;
            if(cnt>t) break;
            while(cnt<=t&&tmp[cnt].num==tmp[cnt-1].num){
                ans[tmp[cnt].id]=1;
                cnt++;
            }
        }
    }
    for(int i=1;i<=t;i++){
        if(ans[i]){
            printf("YES\n");
        }else printf("NO\n");
    }
    return 0;
}

T1,T2题解

暴力枚举
sort+枚举
二分(应该是最快)
setO(x*logx)一遍插入,O(Tlogx)查询,不会T,但是极慢

set的基本操作
s.insert(a);插入a
s.find(a);查询有无a这个数;如果有,返回迭代器,如果没有,返回s.end()

猜数字(number)

Time Limit:2000ms Memory Limit:128MB

题目描述

LYK在玩猜数字游戏。
总共有n个互不相同的正整数,LYK每次猜一段区间的最小值。形如[li,ri]这段区间的数字的最小值一定等于xi。
我们总能构造出一种方案使得LYK满意。直到…… LYK自己猜的就是矛盾的!
例如LYK猜[1,3]的最小值是2,[1,4]的最小值是3,这显然就是矛盾的。
你需要告诉LYK,它第几次猜数字开始就已经矛盾了。

输入格式(number.in)
第一行两个数n和T,表示有n个数字,LYK猜了T次。
接下来T行,每行三个数分别表示li,ri和xi。

输出格式(number.out)
输出一个数表示第几次开始出现矛盾,如果一直没出现矛盾输出T+1。

输入样例
20 4
1 10 7
5 19 7
3 12 8
1 20 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的)。

Hint

建议使用读入优化
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;
}

题解

在线oj
https://www.luogu.org/problemnew/show/2898
oj上全部正确输出0,和考题略有不同

二分+并查集验证
类似数轴染色
二分到哪一句为真,
按x从大到小排序

fa[i]表示与i在同一集合内的最左的坐标-1(-1可以避免处理< 2,2 >这样的区间)
fa[i]+1–i属于同一集合

检查当前区间是否已经在同一集合内,if(fa[r]<l) 说明区间< l,r >在同一集合内
若在,则当前区间已经被另一个更大的x’合并过,与当前区间最小值是x矛盾
若不在,则合并当前区间

注意:当x相同,区间不同时,如果两区间不重合,不符合数都不相等的条件
检查是否矛盾时,用区间的交,因为最小值存在于区间的交之中。
合并时,用区间的并,因为这个大区间的最小值是x
细节处理见代码

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000000+500;
int fa[N],p[N];
struct node{
    int l,r,x;
}a[N];
int n,t;
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;
}

bool cmp(int x,int y){
    return a[x].x>a[y].x;
}
void init(int x){
    for(int i=1;i<=n+5;i++) fa[i]=i;
    for(int i=1;i<=x;i++) p[i]=i;
    sort(p+1,p+x+1,cmp);
    p[x+1]=0;
}
int find(int x){
    if(x==fa[x]) return x;
    else return fa[x]=find(fa[x]);
}
void merge(int l,int r){
    int f=find(l-1);//将l,r的fa数组修改为l-1
    for(int i=r;i>=l;i--){
        int tmp=find(i);
        fa[find(i)]=f;
        i=tmp;//直接跳到原来的find(i) 比暴力修改更快
    }
    return ;
}
bool check(int x){
    init(x);
    int lmax,lmin,rmax,rmin;
    //处理x相同的区间时,需要维护的左右端点值 <lmin,rmax>是区间并  <lmax,rmin>是区间交 
    lmax=lmin=a[p[1]].l;
    rmax=rmin=a[p[1]].r;
    for(int i=2;i<=x;i++){
        int c=p[i],d=p[i-1];
        if(a[c].x!=a[d].x){//这时的lr是i-1的lr
            if(find(rmin)<lmax) return 0;
            merge(lmin,rmax);
            lmax=lmin=a[c].l;//将l,r移动到当前区间
            rmax=rmin=a[c].r;//
        }else {
            if(a[c].x==a[d].x){//如果出现x相等的情况
                lmax=max(lmax,a[c].l);//更新
                lmin=min(lmin,a[c].l);
                rmax=max(rmax,a[c].r);
                rmin=min(rmin,a[c].r);
                if(rmin<lmax) return 0;//区间没有交集,不符合题目中数字互不重复的条件
                if(find(rmin)<lmax) return 0;//检查区间交是否在同一集合内
            }
        }
    }
    if(find(rmin)<lmax) return 0;//检查最后一个区间
    return 1;
}
int main(){
    freopen("number.in","r",stdin);
    freopen("number.out","w",stdout);
    n=read(),t=read();
    for(int i=1;i<=t;i++)
        a[i].l=read(),a[i].r=read(),a[i].x=read();
    int l=-1,r=t+1;
    while(r-l>1){
        int mid=r+l>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    //if(r==t+1) r=0;
    printf("%d",r);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值