NOIP2019模拟赛(二)03.10

T1

题意

题面

给定两个数$a$,$b$求出$b$个$a$相乘的结果。

数据范围

保证$a \leq 99.9999 ,b \leq 25$且$a$的有效数字不超过$6$位。

思路

对于20%的数据

你开$long\quad double$就好了呀。

对于100%的数据

你写高精度就好了呀。
说得很轻巧,但是打比赛的时候花了30分钟。。。
差不多分两个步骤:
1. 把$a$的小数转化为整数,并且求出$a^b$
2. 然后再点一个小数点就好了
坑:0.52要输出成.52。
其他没啥了。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
char A[50010];int b,len,Dot=-1;
int a[5000010];
int t[5000010];
int c[5000010],Len;
int top=0;
void Mul(){//高精度乘法
    int lena=top,lent=Len;
    for(int i=0;i<lent/2;i++) swap(t[i],t[lent-i-1]);
    int u=0;memset(c,0,sizeof(c));
    for(int i=0;i<lena;i++){
        for(int j=0;j<lent;j++){
            c[i+j]+=a[i]*t[j]+u;
            u=c[i+j]/10;
            c[i+j]%=10;
        }
        c[i+lent]=u;u=0;
    }
    Len=lena+lent;
    while(c[Len]==0) Len--;++Len;
    memset(t,0,sizeof(t));
    for(int i=0;i<Len;i++) t[i]=c[i];
    for(int i=0;i<Len/2;i++) swap(t[i],t[Len-i-1]);
}
int main(){
    cin>>A;b=read();len=strlen(A);
    for(int i=0;i<len;i++){
        if(A[i]=='.'){
            Dot=i;//寻找小数点
            continue ;
        }
        t[top]=a[top]=A[i]-'0';
        ++top;
    }
    if(Dot!=-1)
    Dot=len-Dot-1;else Dot=0;//计算出小数点离个位多少位
    for(int i=0;i<top/2;i++) swap(a[i],a[top-i-1]);
    Len=top;
    for(int i=1;i<b;i++){
        Mul();
    }
    Dot=Dot*b;//计算出小数点应该点在哪里
    if(Dot>=Len){//需要输出成.000xxx的形式
        cout<<".";
        for(int i=Len;i<Dot;i++) cout<<'0';
        for(int i=0;i<Len;i++) cout<<t[i];cout<<endl;
    }else{
        for(int i=0;i<Len;i++){
            if(i==Len-Dot){
                cout<<".";//小数点
            }
            cout<<t[i];
        }
        cout<<endl;
    }
    return 0;
}

T2

题意

有两个人很无聊地在玩猜数游戏。某个人想出来一个$n$个正整数的集合,然后选择一个数,让另外一个人猜他选的数的最小质因子。
问:在最优的方案中,最坏情况下需要询问几次,以及最小的询问期望次数?
注:询问期望次数是所有数需要的询问次数的平均值,最坏情况的询问次数为所有数的询问次数的最大值。

思路

很显然这道题和小学奥数题很像。就比如说问有$1$~$n$的数,怎样询问能最少?
显然是$log2$次。
就像这样:↓↓↓


那么对于第二个询问呢?和Luogu的合并果子很像。
Luogu 合并果子
其实就是利用哈夫曼树的原理,每次寻找最小的两个像上面一样$log2$地合并起来就好了。
可以使用$priority$_$queue$的小根堆。
注意:这道题会卡时,所以需要预处理出素数。

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
//queue<int> q;
//set<int> s;
//priority_queue<int> q1;
//priority_queue<int,vector<int>,greater<int> > q2;
//list<int> l;
//stack<int> s;
int n,a[100010],m=0,t[100010],p[100010];
int vis[100010],tot;
priority_queue<int ,vector<int>, greater<int> > Hf;
int GetLog(int x){
    int pp=1;
    while(x!=1){
        pp++;x/=2;
    }
    return pp;
}
void GetPrime(){//预处理素数
    for(int i=2;i<=100000;i++){
        if(vis[i]==0){
            p[++tot]=i;
            for(int j=i;j<=100000;j+=i) vis[j]=1;
        }
    }
}
int main(){
    GetPrime();
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++){
        int fst=sqrt(a[i])+1;
        for(int j=1;j<=tot&&p[j]<=fst;j++){
            if(a[i]%p[j]==0){
                a[i]=p[j];//找到该数的最小质因子
                break;
            }
        }
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++){
        if(a[i]!=a[i-1]) m++;//去重
        t[m]++;
    }
    int ans;
    if(m<=2) puts("0");
    else {
        ans=log2((m-1)<<1);
        write(ans),putchar('\n');
    }
    for(int i=1;i<=m;i++) Hf.push(t[i]);
    ans=0;
    for(int i=1;i<m;i++){
        int x=Hf.top();Hf.pop();
        int y=Hf.top();Hf.pop();
        ans+=x+y;//哈夫曼树思想
        Hf.push(x+y);
    }
    printf("%.5lf\n",(double)ans/(double)n);
    return 0;
}

T3

题意

这和Luogu某题很像

题面

有$n$块石头,有$m$个询问。首先给出这$n$块石头的高度。然后对于每一次的询问有两种:
1. 读入一个整数$x$,询问大于或等于的连续的石头的部分的个数。
2. 读入两个整数$x,y$,表示将第$x$块石头的高度修改为$y$。

样例输入

5 4
8 6 3 5 4
1 5
2 4 1
1 5
1 3

样例输出

2
1
2

样例解释

第一次询问时,洪水高度为$5$ ,露出水面的岩石的编号为${1,2,4}$连续的部分为${1,2}$和${4}$,答案是$2$ 第二次询问时,洪水高度为$5$,露出水面的岩石的编号为${1,2}$连续的部分为${1,2}$,答案是$1$ 第三次询问时,洪水高度为$3$,露出水面的岩石的编号为${1,2,3,5}$连续的部分为${1,2,3}$和${5}$,答案是$2$

思路

对于50%的数据

我们可以采用暴力~(废话)~
所以我们就对于每个查询询问暴力。
结果真的只有50分。。。~(出题人也太狠了吧)~

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define int long long
#define MAXN 100010*4
using namespace std;
inline int read(){
    int res=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
int n,m,a[200010],c[200010];
signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int op,x,y,i=1;i<=m;i++){
        op=read();
        if(op==1){
            x=read();int ans=0;
            for(int j=1;j<=n;j++){
                if(a[j]>=x) ans++;
            }
            for(int j=1;j<=n;j++){
                if(a[j]>=x&&a[j-1]>=x) ans--;
            }
            write(ans);putchar('\n');
        }else{
            x=read();y=read();
            a[x]=y;
        }
    }
    return 0;
}

对于100%的数据

通过观察,我们可以发现,对于每一个询问$Q$,其实就是寻找满足:$h[i-1]<Q \leq h[i]$的个数。
那么我们就可以先暴力预处理出如果$h[i-1]<h[i]$那么$(h[i-1]+1,h[i])$这个答案区间就可加$1$。而对于每一个询问,只需要输出答案区间内的$Ans[Q]$即可。对于每一个修改,影响到的只有$h[i-1]$与$h[i+1]$所以,再重新分别判断一次即可。
注意:每一次的修改需要先清空上一次对于该点的修改。

#include<bits/stdc++.h>
#define MAXN 2000010
using namespace std;
#define int long long
inline int read(){
    char ch=getchar();int res=0,f=1;
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
    return res*f;
}
inline void write(int x){
    if(x<0) putchar('-'),x=-x;
    if(x<10) putchar(x+'0');
    else{
        write(x/10);
        putchar(x%10+'0');
    }
}
int c[MAXN],h[MAXN];
int n,m,N;
int tree[4*MAXN],add[4*MAXN],num[MAXN],Max;
inline void build(int l,int r,int root){//线段树模板走起
    if (l==r){tree[root]=num[l];return;}
    int mid=(l+r)/2;
    build(l,mid,root*2);
    build(mid+1,r,root*2+1);
    tree[root]=tree[root*2]+tree[root*2+1];
}
inline void updata(int l,int r,int root,int x,int ans){
    if(r<x||l>x) return;
    if(l==r&&l==x){tree[root]+=ans;return;}
    int mid=(l+r)/2;
    updata(l,mid,root*2,x,ans);
    updata(mid+1,r,root*2+1,x,ans);
    tree[root]=tree[root*2]+tree[root*2+1];
}
inline void modify(int root,int maxl,int maxr,int l,int r,int v){
    if (maxl>=l&&maxr<=r){add[root]+=v;return;}
    tree[root]+=(min(maxr,r)-max(maxl,l)+1)*v;
    int mid=(maxl+maxr)/2;
    if (l<=mid) modify(root*2,maxl,mid,l,r,v);
    if (mid<r) modify(root*2+1,mid+1,maxr,l,r,v);
}
inline int Search(int maxl,int maxr,int root,int l,int r){
    if (maxl>r||maxr<l) return 0;
    if (l<=maxl&&maxr<=r) return tree[root]+(maxr-maxl+1)*add[root];
    int mid=(maxl+maxr)/2;
    int res=(min(maxr,r)-max(maxl,l)+1)*add[root];
    if (l<=mid) res+=Search(maxl,mid,root*2,l,r);
    if (mid<r) res+=Search(mid+1,maxr,root*2+1,l,r);
    return res;
}//线段树模板结束
int X,top,x[MAXN],k[MAXN],J[MAXN],Las[MAXN];
struct node{
    int x,xx,id,h;
}a[MAXN];
int f[MAXN],g[MAXN],tot,tot2,mx;
bool cmp(node qx,node qy){
    return qx.x<qy.x;
}
signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++){
        a[i].x=read();
        a[i].id=i;
    }
    for(int i=n+1;i<=m+n;i++){
        J[i]=read();
        if(J[i]==2) Las[i]=read();
        a[i].x=read();
        a[i].id=i;
    }
    sort(a+1,a+n+m+1,cmp);
    f[a[1].id]=1;
    for(int i=2;i<=n+m;i++){
        if(a[i].x>a[i-1].x) f[a[i].id]=f[a[i-1].id]+1;
        else f[a[i].id]=f[a[i-1].id];
    }
    Max=f[a[n+m].id];
    build(1,Max,1);
    for(int i=1;i<=n;i++)
        if(f[i-1]<f[i]) modify(1,1,Max,f[i-1]+1,f[i],1);//预处理
    for(int i=n+1;i<=n+m;++i){
        if(J[i]==2){
            int j=Las[i];
            if(f[j-1]<f[j]) modify(1,1,Max,f[j-1]+1,f[j],-1);
            if(j!=n&&f[j]<f[j+1]) modify(1,1,Max,f[j]+1,f[j+1],-1);//清空上一次的修改操作
            f[j]=f[i];//进行新的一次操作
            if(f[j-1]<f[j]) modify(1,1,Max,f[j-1]+1,f[j],1);
            if(j!=n&&f[j]<f[j+1]) modify(1,1,Max,f[j]+1,f[j+1],1);
        }else write(Search(1,Max,1,f[i],f[i])),putchar('\n');//输出该点的值(单点查询)
    }
    return 0;
}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇