一.闲话

本来打的挺开心的,A题速度挺快的。然后。。。G题被疯狂卡精度。。。(绝望),罚时一大堆qwq。我好菜啊qwq

由于打比赛的时候,打的比较快,很多代码都比较粗糙,还请见谅~

二.题解

A.AOE还是单体?

明显是个单峰函数求极值问题(应该可以直接推式子,但是懒)~

我们三分群体攻击次数,然后计算求极值即可~

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+1;
int a[N],n,T;
inline int calc(int x){
    int res=0;
    for(int i=1;i<=n;++i){
        res+=max(0LL,a[i]-x);
    }
    return res+x*T;
}
signed main(){
    scanf("%lld%lld",&n,&T);
    for(int i=1;i<=n;++i){
        scanf("%lld",&a[i]);
    }
    int l=0,r=1e9,res=0;
    while(l<=r){
        int lc=l+(r-l)/3,rc=r-(r-l)/3;
        int L=calc(lc),R=calc(rc);
        if(L<=R){
            r=rc-1;res=L;
        }else{
            l=lc+1;res=R;
        }
    }
    printf("%lld",res);
    return 0;
}

B.k-size字符串

这题一开始式子推错了

首先,我们来考虑下最终字符串的大概形态:

1.abab...(其中a,b由若干个(>=1)'A''B'组成)

2.baba...

那么, 我们就把这两种情况的方案数分别计算出来

我们为了保证合法,先给每个位置的a放一个'A',每个位置的b放一个'B'

然后,我们再把剩下的'A'和'B'放入任意一个a,b里面即可

统计方案:

假设我们现在还剩x个'A',有y个a可以选择,那么方案数为:('B'直接类比即可)

因为每个'A'是等价的,所以,我们这里用隔板法来计算

我们在x个'A'中插入y+1个隔板(假装开头和末尾有一个),相邻两个隔板之间即为一个区间,第i个区间的'A'的个数就是我们放进第i个a的'A'的个数。由于可以放0个进去,所以区间长度可以为0。

那么,方案就是,我们把所有隔板放入后(不考虑开头和末尾),共x+y-1个空,每个空填'A'或者隔板,那么,方案数即为C(x+y-1,y-1)

把两个情况的答案统计一遍,再求和即可(注意判断是否有足够的'A'/'B')

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int N=1e5+1;
int s[N];
inline int ksm(int x,int y){
    int ans=1;
    while(y){
        if(y&1){
            ans=(1LL*ans*x)%mod;
        }
        x=(1LL*x*x)%mod;
        y>>=1;
    }
    return ans;
}
inline int C(int y,int x){
    int res=1;
    for(int i=y+1;i<=x;++i){
        res=(1LL*res*i)%mod;
    }
    for(int i=(x-y);i;--i){
        res=(1LL*res*s[i])%mod;
    }
    return res;
}
int main(){
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    s[0]=s[1]=1;
    for(int i=2;i<=max(n,m);++i){
        s[i]=ksm(i,mod-2);
    }
    //选k个然后依次插入即可
    int ans=0;
    //先a后b
    int mid1=(k+1)/2,mid2=k/2;
    if(n>=mid1&&m>=mid2){
        int A=n-mid1,B=m-mid2,res=1;
        res=(1LL*res*C(mid1-1,n-1))%mod;
        res=(1LL*res*C(mid2-1,m-1))%mod;
        ans=(ans+res)%mod;
    }
    if(n>=mid2&&m>=mid1){
        int A=n-mid2,B=m-mid1,res=1;
        res=(1LL*res*C(mid1-1,m-1))%mod;
        res=(1LL*res*C(mid2-1,n-1))%mod;
        ans=(ans+res)%mod;
    }
    printf("%d",ans);
    return 0;
}

C.白魔法师

明显的一道树形dp问题。

我们设dp[i][0],dp[i][1],dp[i][2]

分别表示以i为根的子树中,没有修改/i修改/子树中有一个修改

那么,我们有转移方程:

前两个很好递推,第三个怎么求?我们注意到,因为1和2一共最多取一个(因为最多修改1次),所以,我们先算出所有儿子中j=0的和,再从j=1/2减去j=0的差值中取最大加上去就好了

最后的答案就是

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1;
struct node{
    int v,nex;
}t[N<<1];
int len,las[N];
bool a[N];
int dp[N][3];
inline void add(int u,int v){
    t[++len]=(node){v,las[u]},las[u]=len;
}
inline void dfs(int now,int fa){
    dp[now][0]=dp[now][2]=a[now];dp[now][1]=1;
    int maxe=0;
    for(int i=las[now];i;i=t[i].nex){
        int v=t[i].v;
        if(v!=fa){
            dfs(v,now);
            if(a[now]){
                dp[now][0]+=dp[v][0];dp[now][2]+=dp[v][0];
                maxe=max(maxe,max(dp[v][1],dp[v][2])-dp[v][0]);
            }
            dp[now][1]+=dp[v][0];
        }
    }
    dp[now][2]+=maxe;
}
int main(){
    int n;
    scanf("%d",&n);
    string x;
    cin>>x;
    for(int i=1;i<=n;++i){
        a[i]=(x[i-1]=='W');
    }
    for(int i=1;i<n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs(1,1);
    int ans=0;
    for(int i=1;i<=n;++i){
        ans=max(ans,max(dp[i][0],dp[i][1]));
        ans=max(ans,dp[i][2]);
    }
    printf("%d",ans);
    return 0;
}

D.抽卡

简单概率。

因为正着算很麻烦,所以我们直接算反面,即抽不到想要的卡的概率。然后用1-抽不到的概率即为抽得到的概率了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+1,mod=1e9+7;
inline int ksm(int x,int y){
    int ans=1;
    while(y){
        if(y&1){
            ans=(1LL*ans*x)%mod;
        }
        x=(1LL*x*x)%mod;
        y>>=1;
    }
    return ans;
}
int a[N],b[N];
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    int ans=1;
    for(int i=1;i<=n;++i){
        scanf("%d",&b[i]);
        b[i]=a[i]-b[i];
        ans=(1LL*ans*b[i])%mod;
        ans=(1LL*ans*ksm(a[i],mod-2))%mod;
    }
    ans=1-ans;
    ans=(ans%mod+mod)%mod;
    printf("%d",ans);
    return 0;
}

E.点击消除

估计正解应该是删去字符串中的所有回文串,不过太懒了,就直接模拟了一遍。。。

代码:

#include<bits/stdc++.h>
using namespace std;
int flag[300001];
int main(){
    string x;
    cin>>x;
    int tim=0;
    while(true){
        ++tim;int len=x.size();bool f=0;
        for(int i=1;i<len;++i){
            if(x[i]==x[i-1]){
                flag[i]=flag[i-1]=tim;f=1;++i;
            }
        }
        if(!f){
            break;
        }
        string y="";
        for(int i=0;i<len;++i){
            if(flag[i]!=tim){
                y+=x[i];
            }
        }
        x=y;
    }
    if(x.size()==0){
        puts("0");
        return 0;
    }
    cout<<x;
    return 0;
}

update:补充一个正解,类似括号匹配问题,把合法的匹配删掉即可~

update2:应大佬要求,来补充下。qwq

我们注意到,我们删除的串一定是这样的:

ABC....TT....CBA【间隔若干个字符】UVW...ZZ...WVU【间隔若干个字符】...
(每个大写字母代表一个字符,不同大写字母代表的字符可能一样,大写字母的数量也可能不一样,只是为了方便说明qwq)

也即是说,我们一定是删除若干个回文串(长度为偶数),那么如果我们把这些字母换成我们常见的括号会怎么样呢?
([{...}])

我们会发现,我们每次删除的,其实就是一个合法的括号的匹配

那么,我们直接类似括号匹配问题,开一个栈,每成功匹配了,就把匹配成功的删去,那么栈里面剩下的就是我们删除之后的字符串辣!

代码:

#include<bits/stdc++.h>
using namespace std;
char sta[300001];int top;
int main(){
    string x;
    cin>>x;
    int len=x.size();
    for(int i=0;i<len;++i){
        if(top&&x[i]==sta[top]){
            --top;
            continue;
        }
        sta[++top]=x[i];
    }
    if(!top){
        puts("0");
        return 0;
    }
    for(int i=1;i<=top;++i){
        cout<<sta[i];
    }
    return 0;
}

F.疯狂的自我检索者

最小情况就是剩余m个人都只给1分,最大情况就是剩余m个人都给5分。

分别计算即可

代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int sum=0;
    for(int i=1;i<=n-m;++i){
        int x;
        scanf("%d",&x);
        sum+=x;
    }
    double ming=sum+m,maxe=sum+5*m;
    ming/=double(n),maxe/=double(n);
    printf("%.5lf %.5lf",ming,maxe);
    return 0;
}

G.解方程

这题精度把我整惨了qwq(不知道各位大佬怎么弄得(感觉是我人丑所以代码丑))

观察后不难发现,该式是明显的一个递增函数,而且x趋近0的时候,值趋近0,c>=1,所以必有解,直接二分就好了。

然而,二分会挂(应该是我自己的锅qwq)

所以,我们考虑玄学的做法:

我们把整数部分和小数部分分开二分做,这样,就不怕精度了qwq(别跟我说long double,我long double也出锅了qwq,气死)

代码:

#include<bits/stdc++.h>
using namespace std;
const double eps=1e-10;
int a,b,c;
inline double power(double x,int y){
    double res=1;
    while(y--){
        res*=x;
    }
    return res;
}
int main(){
    scanf("%d%d%d",&a,&b,&c);
    int L=0,R=1e9,po=0;
    while(L<=R){
        int mid=(L+R)>>1;
        double res=power(mid,a)+b*log(mid)-c;
        if(res<=0){
            L=mid+1;
        }else{
            R=mid-1;
        }
    }
    po=min(L,R);
    double l=eps,r=1;
    while(r-l>=eps){
        double mid=(l+r)/2.0;
        double res=power(mid+po,a)+b*log(mid+po)-c;
        if(res>=0){
            r=mid;
        }else{
            l=mid;
        }
    }
    printf("%.14lf",l+po);
    return 0;
}

H.神奇的字母(二)

直接读入,然后统计个数即可

代码:

#include<bits/stdc++.h>
using namespace std;
int tim[26];
int main(){
    string x;
    while(cin>>x){
        int len=x.size();
        for(int i=0;i<len;++i){
            ++tim[x[i]-'a'];
        }
    }
    int maxe=0,id=0;
    for(int i=0;i<26;++i){
        if(tim[i]>maxe){
            maxe=tim[i];id=i;
        }
    }
    cout<<char(id+'a');
    return 0;
}

I.十字爆破

一个很简单的统计问题

我们设l[i]表示第i列的所有数的和,h[i]表示第i行所有数的和,a[i]表示第i个数(编号后)的值,那么第i行和第j列的所有数的和即为

维护好数组后,按公式输出即可

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+1;
#define int long long
int l[N],h[N],a[N];
signed main(){
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            int x;
            scanf("%lld",&x);h[i]+=x,l[j]+=x;
            a[(i-1)*m+j]=x;
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            printf("%lld ",h[i]+l[j]-a[(i-1)*m+j]);
        }
        puts("");
    }
    return 0;
}

J.异或和之和

还是上套路:按位统计

假设有k个二进制第i位为1的数,那么第i位的贡献为:

直接每位都统计下贡献,再加起来即可

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int tot[60];
inline long long C(int x,int y){
    if(x<y){
        return 0;
    }
    y=x-y;
    long long res=1;
    for(int i=y+1;i<=x;++i){
        res=(1LL*res*i);
    }
    for(int i=2;i<=x-y;++i){
        res/=i;
    }
    return res%mod;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        long long x;
        cin>>x;
        for(int j=0;j<=59;++j){
            tot[j]+=(x&1);
            x>>=1;
        }
    }
    long long ans=0,now=1;
    for(int i=0;i<=59;++i){
        //1个1 or 3个1
        long long res=(1LL*C(tot[i],1)*C(n-tot[i],2))%mod+C(tot[i],3);
        res%=mod;
        ans+=(1LL*res*now)%mod;
        ans%=mod;
        now=(2LL*now)%mod;
    }
    printf("%d",ans);
    return 0;
}