牛客小白月賽25【A - J】

題目鏈接:https://ac.nowcoder.com/acm/contest/5600
沉寂了快三個月了,3個月來的第一篇博客~ (Minecraft mod 還沒做好,如果後面有機會的話,我可以更新mod開發教程,扯偏了
這場的話,手感不好,瘋狂罰時,而且寫的也很慢,最後才7題…後來發現卡我的那題居然是沒有push,還有一題被卡精度,過題人最少的那題我看都沒看,就是個排列組合而已,可惜…



我的程序頭(節省篇幅)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<string>
#define ls k<<1,l,mid
#define rs k<<1|1,mid+1,r
#define mp(x,y) make_pair(x,y)
#define r(x) read(x)
#define rr(x,y) read(x);read(y)
#define rrr(x,y,z) read(x);read(y);read(z)
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define pb(x) push_back(x)
#define sss(str) scanf("%s",str+1)
#define ssf(x) scanf("%lf",&x)
#define aLL(x) x.begin(),x.end()
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef double dd;
typedef pair<int,int> pt;
const int N=2e5+500;
const int M=2e3+50;
const int INF=0x7fffffff;
const int mod=1e9+7;
const int sz=18;
const double eps=1e-8;

template<class T>
inline void read(T &x)
{
    char c;x=1;
    while((c=getchar())<'0'||c>'9') if(c=='-') x=-1;
    T res=c-'0';
    while((c=getchar())>='0'&&c<='9') res=res*10+c-'0';
    x*=res;
}

A - AOE還是單體?

當剩下的怪的數量大於等於x的時候,顯然用第二個技能是不虧甚至是賺的。
那就從一開始瘋狂用二技能,當只剩下x-1只怪時,再用第一技能唄~

int n,m;
char str[N];
int f[N];
int main()
{
    rr(n,m);
    FOR(i,1,n) r(f[i]);
    sort(f+1,f+n+1);
    LL ans=0;
    if(m>n){
        FOR(i,1,n){
            ans+=f[i];
        }
        cout<<ans<<endl;
        return 0;
    }
    for(int i=n;i>=n-m+2;i--){
        ans+=f[i];
    }
    ans-=1ll*(m-1)*f[n-m+1];
    ans+=1ll*f[n-m+1]*m;
    cout<<ans<<endl;
    return 0;
}


B - k-size字符串

這題就是我沒看的那題,有點可惜…
這樣想,k最小是2(此時a一邊,b一邊)k最大是多少?
(ababab這樣拍顯然利用率最高,但n和m肯定不會都那麼接近)
不難發現,和 min(a,b) 有關 LL maxx=tmp+min(max(a,b),tmp+1);
然後,肯定是a b各貢獻一半段,如果k爲奇數那就a或b多貢獻一段
怎麼算這個貢獻呢?(我用的是高中學的隔板法
在這裏插入圖片描述
假如說有7個相同的球,我們想分爲3段,那就插3-1個板子進去,可以放的位置有7-1個(根據排列組合得:C62C_{6}^{2} ) 以此類推即可

LL qpow(LL a,LL p)
{
    LL res=1;
    while(p){
        if(p&1) res=res*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return res;
}
LL inv(LL x)
{
    return qpow(x,mod-2);
}
LL hhh(LL n,LL m)
{
    LL res=1;
    FOR(i,1,m){
        res=res*(n-i+1)%mod;
        res=res*inv(i)%mod;
    }
    return res;
}
int main()
{
    LL a,b,c;
    rrr(a,b,c);
    LL tmp=min(a,b);
    LL maxx=tmp+min(max(a,b),tmp+1);
    LL ans=0;
    if(c>=2&&c<=maxx){
        tmp=c/2;
        if(c&1){
            if(b>tmp){
                ans=(ans+hhh(a-1,tmp-1)*hhh(b-1,tmp)%mod)%mod;
            }
            if(a>tmp){
                ans=(ans+hhh(a-1,tmp)*hhh(b-1,tmp-1)%mod)%mod;
            }
        }
        else{
            ans=hhh(a-1,tmp-1)*hhh(b-1,tmp-1)%mod*2%mod;
        }
    }
    cout<<ans<<endl;
    return 0;
}

C - 白魔法師

這題不知道我是不是搞複雜了( bfs+並查集
最後因爲bfs時沒有push,瘋狂WA
思路都是,把一開始白連通塊的作爲一個集合,黑的不管。
然後找所有的黑的,假如把它變白了,那和它相連的白連通塊就能合併起來 因爲一開始記錄了,所以查詢O(1)解決

int n,m;
char str[N];
int f[N];
int dad[N];
vector<int> v[N];
bool vis[N];
int seek(int k)
{
    return dad[k]==k?k:dad[k]=seek(dad[k]);
}
void bfs(int x)
{
    queue<int> q;
    q.push(x);
    vis[x]=1;
    int ans=1;
    while(q.size()){
        int now=q.front();
        q.pop();
        for(int y:v[now]){
            if(!vis[y]&&str[y]=='W'){
                vis[y]=1;
                q.push(y);
                ans++;
                int xx=seek(x);
                int yy=seek(y);
                if(xx!=yy){
                    dad[xx]=yy;
                }
            }
        }
    }
    f[seek(x)]=ans;
}
int main()
{
    r(n);
    sss(str);
    FOR(i,1,n-1){
        int a,b;
        rr(a,b);
        v[a].pb(b);
        v[b].pb(a);
    }
    FOR(i,1,n) dad[i]=i;
    FOR(i,1,n){
        if(str[i]=='W'&&!vis[i]){
            bfs(i);
        }
    }
    int ans=0;
    FOR(i,1,n){
        ans=max(ans,f[seek(i)]);
//        cout<<f[dad[i]]<<endl;
    }
    FOR(i,1,n){
        if(str[i]=='B'){
            int res=1;
            for(int x:v[i]){
                if(str[x]=='W'){
                    res+=f[dad[x]];
                }
            }
            ans=max(ans,res);
        }
    }
    cout<<ans<<endl;
    return 0;
}

D - 抽卡

這題逆向思維,求出得不到的概率,然後用1減就ok
用到了逆元

int n,m;
char str[N];
int f[N],g[N];
LL qpow(LL a,LL p)
{
    LL res=1;
    while(p){
        if(p&1) res=res*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return res;
}
LL inv(LL a)
{
    return qpow(a,mod-2);
}
int main()
{
    r(n);
    FOR(i,1,n){
       r(f[i]);
    }
    FOR(i,1,n){
        r(g[i]);
    }
    LL ans=1;
    LL a=1,b=1;
    FOR(i,1,n){
        a=a*(f[i]-g[i])%mod;
        b=b*f[i]%mod;
        ans=ans*inv(f[i])%mod;
    }
    cout<<(b-a+mod)%mod*ans%mod<<endl;
    return 0;
}


E - 點擊消除

和括號匹配差不多

int n,m;
char str[N];
char ans[N];
int f[N];
int main()
{
    sss(str);
    n=strlen(str+1);
    stack<char> s;
    m=0;
    FOR(i,1,n){
        if(m>0){
            if(ans[m]==str[i]){
                --m;
                continue;
            }
        }
        ans[++m]=str[i];
    }
    if(m==0) cout<<0;
    FOR(i,1,m) cout<<ans[i];
    cout<<endl;
    return 0;
}


F - 瘋狂的自我檢索者

最大的可能就是不知道的全部投5分,最小的就是不知道的全部投1分

int n,m;
char str[N];
int f[N];
int main()
{
    rr(n,m);
    LL sum=0;
    FOR(i,1,n-m){
        r(f[i]);
        sum+=f[i];
    }
    double ans1=(sum+5*m)*1.0/n;
    double ans2=(sum+m)*1.0/n;
    printf("%.5f %.5f\n",ans2,ans1);
    return 0;
}


G - 解方程

一開始就開的這題,沒想到被卡精度了
我甚至一度懷疑自己二分寫錯了(最後就判斷循環了2000次就跳出即可)

int n,m;
char str[N];
LL f[N];
int main()
{
    int a,b,c;
    rrr(a,b,c);
    double l=0,r=1e9;
    int cnt=0;
    while(r-l>=eps){
        cnt++;
        if(cnt>=2000) break;
        double mid=(l+r)/2;
//        cout<<mid<<endl;
        double sum=1;
        FOR(i,1,a){
            sum*=mid;
        }
        sum+=b*log(mid);
        if(sum>=c){
            r=mid-eps;
        }
        else{
            l=mid+eps;
        }
    }
    printf("%.7f\n",l);
    return 0;
}


H - 神奇的字母(二)

這題輸入方式有點奇葩

int n,m;
char str[N];
int f[N];
int main()
{
    while(~scanf("%s",str+1)){
        int len=strlen(str+1);
        for(int i=1;i<=len;i++){
//            cout<<'*'<<str[i]<<'*'<<endl;
            if(str[i]>='a'&&str[i]<='z'){
                f[str[i]-'a'+1]++;
            }
        }
    }
    char ans='a';
    int maxx=0;
    FOR(i,1,26){
        if(f[i]>maxx){
            maxx=f[i];
            ans=(char)('a'+i-1);
        }
    }
    cout<<ans<<endl;
    return 0;
}


I - 十字爆破

我這樣寫的還不夠簡潔,其實一個數組f[N]就夠了
就是個前綴和的思想(我寫的還不是前綴和,我是統計)

int n,m;
char str[N];
char ans[N];
int f[N];
LL a[N],b[N];
int get(int x,int y)
{
    return f[m*x+y];
}
int main()
{
    rr(n,m);
    FOR(i,0,n-1){
        FOR(j,0,m-1){
            r(f[m*i+j]);
        }
    }
    FOR(i,0,n-1){
        a[i]=0;
        FOR(j,0,m-1){
            a[i]+=get(i,j);
        }
    }
    FOR(i,0,m-1){
        b[i]=0;
        FOR(j,0,n-1){
            b[i]+=get(j,i);
        }
    }
    FOR(i,0,n-1){
        FOR(j,0,m-1){
            cout<<a[i]+b[j]-get(i,j)<<' ';
        }
        cout<<endl;
    }
    return 0;
}

J - 異或和之和

按位考慮,假如現在是第i位,有x個1,y個0 (x+y=n)
那這一位有效的情況無非就是【1 0 0】或【1 1 1】 也就是 Cx1Cy2+Cx3C_{x}^{1}*C_{y}^{2}+C_{x}^{3}
其實好像不需要逆元,我又多此一舉了

int n,m;
char str[N];
int f[M];
LL qpow(LL a,LL p)
{
    LL res=1;
    while(p){
        if(p&1) res=res*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return res;
}
LL inv(LL a)
{
    return qpow(a,mod-2);
}
int main()
{
    r(n);
    FOR(i,1,n){
        LL a;
        r(a);
        FOR(i,0,62){
            if((a>>i)&1){
                f[i]++;
            }
        }
    }
    LL ans=0;
    FOR(i,0,62){
        LL x=f[i],y=n-f[i];
        if(x>=1&&y>=2){ //1 0 0
            ans=(ans+(1ll<<i)%mod*x%mod*y%mod*(y-1)%mod*inv(2)%mod)%mod;
        }
        if(x>=3){ // 1 1 1
            ans=(ans+(1ll<<i)%mod*x%mod*(x-1)%mod*(x-2)%mod*inv(6)%mod)%mod;
        }
    }
    cout<<ans<<endl;
    return 0;
}


題目不難,但我太不熟練了…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章