Educational Codeforces Round 75 (Rated for Div. 2)

风格不统一 提交于 2019-12-02 11:21:26

(体验到了胡出一道题但是写锅的绝望呢)

 

A:

送分题。

#include<bits/stdc++.h>
#define maxn 100005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
 
using namespace std;
bool may[30]; char str[maxn];
 
inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
int main(){
    int T=read();
    while(T--){
        scanf("%s",str+1);
        int n=strlen(str+1);
        memset(may,0,sizeof(may));
        for(int i=1;i<=n;){
            int j=i; while(str[j]==str[j+1]) j++;
            if((j-i+1)%2) may[str[i]-'a']=1;
            i=j+1;
        }
        for(int i=0;i<26;i++) 
            if(may[i]) printf("%c",(char)(i+'a'));
        cout<<endl;
    }
    return 0;
}
A

 

B:

送分题。因为根据样例猜结论wa了一发。

看到有人dp???这难道不是瓶颈在读入的结论题?

#include<bits/stdc++.h>
#define maxn 100005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
 
using namespace std;
char str[maxn];
 
inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
int main(){
    int T=read();
    while(T--){
        int n=read(),n1=0,n0=0,t1=0;
        for(int i=1;i<=n;i++){
            scanf("%s",str+1);
            int len=strlen(str+1);
            if(len%2) t1++;
            for(int j=1;j<=len;j++){
                if(str[j]=='1') n1++;
                else n0++;
            }
        } 
        if(n0<n1) swap(n0,n1);
        if(t1%2){
            if(n0%2==n1%2) cout<<n-1<<endl;
            else cout<<n<<endl;
        }
        else if(t1==0){
            if(n0%2==1) cout<<n-1<<endl;
            else cout<<n<<endl;
        }
        else cout<<n<<endl;
    }
    return 0;
}
B

 

C:

送分题。

注意到奇数之间的顺序不会变化,偶数之间的顺序也不会变化,于是做一下归并排序的合并操作即可。

#include<bits/stdc++.h>
#define maxn 300005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
 
using namespace std;
char str[maxn];
int t1[maxn],t0[maxn];
 
inline int read(){
    int x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
int main(){
    int T=read();
    while(T--){
        scanf("%s",str+1);
        int n=strlen(str+1);
        t1[0]=t0[0]=0;
        for(int i=1;i<=n;i++){
            if((str[i]-'0')%2) t1[++t1[0]]=str[i]-'0';
            else t0[++t0[0]]=str[i]-'0';
        }
        int p1=1,p0=1;
        while(p1<=t1[0] || p0<=t0[0]){
            if(p1>t1[0]) printf("%d",t0[p0]),p0++;
            else if(p0>t0[0]) printf("%d",t1[p1]),p1++;
            else{
                if(t1[p1]<t0[p0]) printf("%d",t1[p1]),p1++;
                else printf("%d",t0[p0]),p0++;
            }
        }
        cout<<endl;
    }
    return 0;
}
C

 

D:

送分题。

中位数没有太多性质,要么枚举答案要么二分答案。

二分完判一下边界求能达到目标的最小价值即可。

由于没判一个人wa了一发,边界没判好又wa了一发。

#include<bits/stdc++.h>
#define maxn 200005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
 
using namespace std;
ll n; struct node{ll l,r;}a1[maxn],a2[maxn],e[maxn];
 
inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
inline bool cmp1(node x,node y){return x.r<y.r;}
inline bool cmp2(node x,node y){return x.l<y.l;}
 
inline ll calc(ll x){
    ll sum=0,n1=0,n2=0;
    for(ll i=1;i<=n;i++)
        if(e[i].r<x) sum+=e[i].l,n1++;
    for(ll i=1;i<=n;i++)  
        if(e[i].l>x) sum+=e[i].l,n2++;
    if(n1>n/2) return (1ll<<60);
    if(n2>n/2) return 0;
    ll tp=n/2-n1;
    for(ll i=1;i<=n;i++)
        if(a1[i].l<=x && a1[i].r>=x){
            if(tp) sum+=a1[i].l,tp--;
            else sum+=x;
        }
    return sum;
}
 
int main(){
    ll T=read();
    while(T--){
        n=read(); ll s=read();
        for(ll i=1;i<=n;i++){
            e[i].l=a1[i].l=a2[i].l=read();
            e[i].r=a1[i].r=a2[i].r=read();
        }
        sort(a1+1,a1+n+1,cmp2);
        if(n==1){cout<<min(s,e[1].r)<<endl;continue;}
        ll l=1,r=(ll)(1e9),ans=0;
        while(l<=r){
            ll mid=l+r>>1;
            //cout<<mid<<":"<<calc(mid)<<endl;
            if(calc(mid)<=s) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
D

 

E:

送分题。但我把分还给了这个世界。

实际上所有人被m值划分成了若干个阶梯,每个阶梯都会在一起投票。

考虑$m_{i}$最大的那个人。如果他前面的人数还不到$m_{i}$,那不管怎么样都必须花钱买他,否则暂时没有必要买他。

那么按m值排序后倒着考虑每个人,维护一个以$p_{i}$为关键字的小根堆,如果要买就买若干个最小的。

容易用归纳法证明该算法的正确性。复杂度$O(nlogn)$。

(当时是0:40,我在大脑混乱的情况下想写一个给数组直接排序的$O(n^{2}logn)$的做法过E1。)

(然后我没考虑如果给后面的东西按p排序会把有序的m搞乱掉,于是我就对着一发wa on 2沉思到考试结束。)

(这种明显是我的锅当然要甩给石神了!谁让它那么可爱!)

#include<bits/stdc++.h>
#define maxn 200005
#define maxm 500005
#define inf 0x7fffffff
#define ll long long
 
using namespace std;
ll n;
struct node{ll m,p,id;}a[maxn];
priority_queue<ll> q;
 
inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
inline bool cmp1(node x,node y){return x.m<y.m;}
inline bool cmp2(node x,node y){return x.p<y.p;}
 
int main(){
    ll T=read();
    while(T--){
        n=read();
        for(ll i=1;i<=n;i++) a[i].m=read(),a[i].p=read();
        sort(a+1,a+1+n,cmp1);
        ll num=0,ans=0;
        for(ll i=n;i>=1;i--){
            q.push(-a[i].p);
            while(i-1+num<a[i].m) ans+=-q.top(),q.pop(),num++;
        }
        printf("%I64d\n",ans);
        while(!q.empty()) q.pop();
    }
    return 0;
}
E

 

F:

注意到对于一个红块,它可选的白块范围是确定的。

对于一个$Q_{i}$,如果我们确定了放哪个红块,那么能放的白块数就确定下来了。

那么我们可以预处理每个红块对应的放$i$个白块的方案数。

推组合数复杂度显然不对,但可以考虑生成函数,它的本质也是解决有限或无限元素的组合问题。

如果一个白块的$h_{i}$出现了多于两次,由于要求严格递增,那么它最多只能在两边各放一个。于是我们可以把它当做出现了两次。

根据生成函数相关知识,我们得到:

1.若一个$h_{i}$出现一次,那么它在方案中对应一个$(1+2x)$,表示不放/放左边或放右边。

2.若一个$h_{i}$出现两次,那么它在方案中对应一个$(1+2x+x^{2})$,表示不放/放左边或放右边/放两边。

将这些多项式卷起来,$x^{m}$项的系数就是放m个白块的方案数。

一开始我写了一个卷积快速幂进行$logn$次$NTT$。然后T on 97。(幸好我考场上压根没写)

然后在欧神的教导下我发现:进行一次$NTT$后做点值快速幂就完事了,我根本没学明白。

一些非常致命的细节:

1.写多项式应用题时一定要开一个临时数组然后对其$NTT$,不然会将原数组变换成点值表示。

2.如果你不想开临时数组可以每次把用到的数组清空一遍,注意清空一定要清到$NTT$时的len而不是需要保留的len。

3.强烈不建议$NTT$过去后再$NTT$回来,1s跑不了几次$NTT$。

#include<bits/stdc++.h>
#define maxn 300005
#define maxm 1<<21|1
#define inf 0x7fffffff
#define mod 998244353
#define g 3
#define ll long long
#define rint register ll
 
using namespace std;
ll mx=3e5,h1[maxn],h2[maxn],ind[maxm];
ll p1[maxm],p2[maxm],tq[maxm],tp[maxm];
ll res[5][maxm];
 
inline ll read(){
    ll x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
 
inline ll mo(ll x){return x>=mod?x-mod:x;}
inline ll power(ll a,ll b){ll ans=1;while(b){ans=(b&1)?ans*a%mod:ans,a=a*a%mod,b>>=1;}return ans;}
inline void copy(ll *a,ll *b){for(rint i=0;i<(maxm>>1);i++) a[i]=b[i];}
inline void clear(ll *a,ll x){for(rint i=0;i<(maxm>>1);i++) a[i]=0;a[0]=1,a[1]=(x?2:0),a[2]=(x==2);} //不能使用sizeof(*a)的形式 
inline void print(ll *a){for(rint i=0;i<=10;i++) cout<<a[i]<<" ";cout<<endl;}
 
namespace P{
    inline void ntt(ll *a,ll n,ll op){
        for(rint i=0;i<n;i++) ind[i]=(i&1)?((ind[i>>1]>>1)|(n>>1)):(ind[i>>1]>>1);
        for(rint i=0;i<n;i++) if(ind[i]>i) swap(a[i],a[ind[i]]);
        for(rint l=2;l<=n;l<<=1){
            ll p=power(g,(mod-1)/l);
            if(op==-1) p=power(p,mod-2);
            for(rint i=0;i<n;i+=l)
                for(ll j=i,w=1,t;j<i+(l>>1);j++,w=w*p%mod)
                    t=a[j+(l>>1)]*w%mod,a[j+(l>>1)]=mo(a[j]-t+mod),a[j]=mo(a[j]+t);
        } 
    }
    inline ll conv(ll *a,ll n,ll *b,ll m){
        ll len=1,lim=min(n+m,mx);
        while(len<=lim) len<<=1;
        ntt(a,len,1),ntt(b,len,1);
        for(rint i=0;i<len;i++) a[i]=a[i]*b[i]%mod;
        ntt(a,len,-1); ll inv=power(len,mod-2);
        for(rint i=0;i<len;i++) a[i]=a[i]*inv%mod;
        return lim; 
    }
    inline ll conp(ll *a,ll b,ll c){
        if(b==0){clear(a,0);return 0;}
        ll len=1,lim=min(c*b,(ll)mx); 
        while(len<=lim) len<<=1; ntt(a,len,1); 
        for(rint i=0;i<len;i++) a[i]=power(a[i],b);
        ntt(a,len,-1); ll inv=power(len,mod-2);
        for(rint i=0;i<len;i++) a[i]=a[i]*inv%mod;
        return lim;
    }
}
 
int main(){
    ll n=read(),k=read();
    for(rint i=1;i<=n;i++) h1[i]=read();
    for(rint i=0;i<k;i++) h2[i]=read();
    sort(h1+1,h1+1+n),sort(h2,h2+k);
    for(rint i=0;i<k;i++){
        ll l1=0,l2=0,now=1,n1=0,n2=0;
        while(now<=n){
            if(h1[now]>=h2[i]) break;
            ll j=now; while(j+1<=n && h1[j]==h1[j+1]) j++;
            if(j==now) n1++; else n2++; now=j+1;
        }
        clear(p1,1),l1=1;l1=P::conp(p1,n1,l1);
        clear(p2,2),l2=2;l2=P::conp(p2,n2,l2);
        //print(p1),print(p2);
        l1=P::conv(p1,l1,p2,l2),copy(res[i],p1);
        //print(res[i]);
    }
    ll Q=read();
    while(Q--){
        ll q=read(),ans=0;
        for(rint i=0;i<k;i++){
            ll num=(q-2*h2[i])/2;
            if(num-1<0) break;
            if(num-1>(ll)mx) continue;
            ans=mo(ans+res[i][num-1]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
F

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!