Atcoder Grand Contest 021 题解

Problem A. (此处省略)反正怎么搞都行了。。。

Problem B. 对于每个点,枚举其他点,那么选的点一定是在垂直平分线的一侧。由于R 特别大,所以可以把这个垂直平分线移到这个点上,就相当于一堆可行极角区间求交

也可以 O(nlogn) ,大概是把凸包搞出来然后答案就出来了。。。

#include<bits/stdc++.h>
#define maxn 110
using namespace std;
typedef double ldb;
const ldb eps=1e-7;
const ldb pi=acos(-1.0);
ldb nx[maxn],ny[maxn];
int n;
int dcmp(ldb d){
    return fabs(d)<eps?0:(d<0?-1:1);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%lf%lf",&nx[i],&ny[i]);
    for(int i=1;i<=n;++i){
        ldb mn=0,mx=pi;
        int id=(i==1?2:1);
        ldb ang=atan2(ny[id]-ny[i],nx[id]-nx[i]);
        for(int j=1;j<=n;++j)if(i!=j&&j!=id){
            ldb ang1=atan2(ny[j]-ny[i],nx[j]-nx[i]);
            ldb a=ang-ang1;
            if(dcmp(a-pi)==0||dcmp(a+pi)==0)mn=1e20;
            if(dcmp(a-pi)>0)a-=2*pi;
            else if(dcmp(-pi-a)>0)a+=2*pi;
            if(dcmp(a)<0)mn=max(mn,-a);
            else mx=min(mx,pi-a);
        }
//      printf("[%.3lf,%.3lf]",mn,mx);
        if(dcmp(mn-mx)>=0)puts("0.0");
        else printf("%.10lf\n",(mx-mn)/(2*pi));
    } 
}

Problem C.可以发现,一定是先贪心放2*2的小方块,多的部分用一种方块来补。最后要特判右下角的3*3方格。

#include<bits/stdc++.h>
using namespace std;
char s[1010][1010];
int n,m,a,b;
int main(){
    scanf("%d%d%d%d",&n,&m,&a,&b);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)s[i][j]='.';
    if(n&1)for(int i=1;i<m&&a;i+=2)s[n][i]='<',s[n][i+1]='>',a--;
    if(m&1)for(int i=1;i<n&&b;i+=2)s[i][m]='^',s[i+1][m]='v',b--;
    for(int i=1;i<n;i+=2)
        for(int j=1;j<m;j+=2){
            if(a>=2){
                a-=2;
                s[i][j]='<',s[i][j+1]='>';
                s[i+1][j]='<',s[i+1][j+1]='>';
            } else if(b>=2){
                b-=2;
                s[i][j]='^',s[i][j+1]='^';
                s[i+1][j]='v',s[i+1][j+1]='v';
            } else if(a&&b&&i==n-2&&j==m-2){
                a--,b--;
                s[i][j]='<',s[i][j+1]='>',s[i][j+2]='^';
                s[i+1][j]='^',s[i+1][j+2]='v';
                s[i+2][j]='v',s[i+2][j+1]='<',s[i+2][j+2]='>';
            } else if(a){
                a--;
                s[i][j]='<',s[i][j+1]='>';
            } else if(b){
                b--;
                s[i][j]='^',s[i+1][j]='v';
            }
        }
    if(a||b)return puts("NO"),0;
    puts("YES");
    for(int i=1;i<=n;++i)puts(s[i]+1);
}

Problem D.直接 dp[i][j][k] 表示左边匹配到 i ,右边匹配到j,还剩 k 次。时间复杂度O(n3)

#include<bits/stdc++.h>
using namespace std;
int dp[310][310][310],n;
char s[310];
int dfs(int l,int r,int k){
    if(k<0)return -1000000007;
    if(l>r)return 0;
    else if(l==r)return 1;
    if(~dp[l][r][k])return dp[l][r][k];
    dp[l][r][k]=max(dfs(l+1,r,k),dfs(l,r-1,k));
    dp[l][r][k]=max(dp[l][r][k],dfs(l+1,r-1,k-(s[l]!=s[r]))+2);
//  printf("{%d,%d,%d,%d}",l,r,k,dp[l][r][k]);
    return dp[l][r][k];
}
int main(){
    memset(dp,-1,sizeof(dp));
    scanf("%s%d",s+1,&n);
    printf("%d\n",dfs(1,strlen(s+1),n));
}

Problem E.
首先R< B肯定不可行。
可以发现,一个可行的方案一定满足R-B+match≤n,其中match表示R,B最大匹配数。

假设我们把这个序列理解为平面上(0,0)到(R,B)的一个路径,设现在在(x,y)
当R>B,y>x时若要满足条件,一定有R-B+x+min(B-y,R-x)≤n
即不能有y-x≤R-n+1

R=B同理。
考虑如何算这个,可以用反射法,就是到这个直线就开始分成2条路径。详细看官方题解。

#include<bits/stdc++.h>
#define maxn 1001000
#define mod 998244353
using namespace std;
int fac[maxn],inv[maxn],n,m,K,M;
int qpow(int a,int b){
    int ans=1,tmp=a;
    for(;b;b>>=1,tmp=1ll*tmp*tmp%mod)
        if(b&1)ans=1ll*ans*tmp%mod;
    return ans;
}
int C(int x,int y){
    if(x>y||x<0)return 0;
    return 1ll*fac[y]*inv[x]%mod*inv[y-x]%mod;
}
int main(){
    scanf("%d%d",&n,&K),M=K,fac[0]=inv[0]=1;
    for(int i=1;i<=M;++i)fac[i]=1ll*fac[i-1]*i%mod;
    inv[M]=qpow(fac[M],mod-2);
    for(int i=M-1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    int ans=0;
    for(int i=0;i<=K-i;++i){
        int r=K-i,b=i;
        if(r-b+min(r,b)<n)continue; 
        if(r>b)ans=(1ll*ans+C(r,r+b)-C(r+r-n+1,r+b)+mod)%mod;
        else ans=(1ll*ans+C(r,r+b-1)-C(r+b-n+1,r+b-1)+mod)%mod;
    }
    printf("%d\n",ans);
}

Problem F.令dp[i][j]表示i列j行的图的答案,且这j行非空。则有如下转移:

1.dp[i][j]->dp[i+1][j],此时有1+C(j,1)+C(j,2)种方案。
2.dp[i][j]->dp[i+1][j+k](k>1),此时只需要考虑新列的方案数,如果最大值放在了j行中的一个,可以将这一行视为新建行,否则可以将最大值多取一个,反正可以发现答案就是i+k+2中选k+2个,即C(i+k+2,k+2)

然后发现这是个卷积的形式。完了。复杂度 O(nmlogn)

#include<bits/stdc++.h>
#define mod 998244353
#define maxn 8200
using namespace std;
typedef long long ll;
int n,m,dp[210][maxn<<1],wn[2][maxn<<1],bh[maxn<<1];
int A[maxn<<1],M,fac[maxn<<1],k,inv[maxn<<1];
int qpow(int a,int b){
    int ans=1;
    for(;b;b>>=1,a=1ll*a*a%mod)
        if(b&1)ans=1ll*ans*a%mod;
    return ans;
}
void init(){
    for(int i=1,p;i<M;i<<=1){
        wn[0][i]=1,p=qpow(3,(mod-1)/(i<<1));
        for(int j=1;j<i;++j)wn[0][i+j]=(ll)wn[0][i+j-1]*p%mod;
        wn[1][i]=1,p=qpow(3,mod-1-(mod-1)/(i<<1));
        for(int j=1;j<i;++j)wn[1][i+j]=(ll)wn[1][i+j-1]*p%mod;
    }
}
void fft(int h[],int len,int flag){
    for(int i=0;i<len;++i)if(i<bh[i])swap(h[i],h[bh[i]]);
    for(int i=1;i<len;i<<=1)
        for(int j=0;j<len;j+=(i<<1))
            for(int k=0;k<i;++k){
                int x=h[j+k],y=(ll)h[j+k+i]*wn[flag][i+k]%mod;
                h[j+k]=(x+y)%mod,h[j+k+i]=(x-y+mod)%mod;
            }
    if(flag==1){
        int iv=qpow(len,mod-2);
        for(int i=0;i<len;++i)h[i]=1ll*h[i]*iv%mod;
    }
}
int C(int x,int y){
    if(x>y||x<0)return 0;
    return (ll)fac[y]*inv[x]%mod*inv[y-x]%mod;
}
int main(){
    scanf("%d%d",&n,&m),M=1,k=0,fac[0]=inv[0]=1;
    while(M<2*n)M<<=1,k++;
    for(int i=1;i<=n+2;++i)fac[i]=1ll*fac[i-1]*i%mod;
    inv[n+2]=qpow(fac[n+2],mod-2);
    for(int i=n+1;i>=1;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
    for(int i=0;i<M;++i)bh[i]=(bh[i>>1]>>1)|((i&1)<<(k-1));
    init();
    dp[0][0]=1;
    for(int i=0;i<m;++i){
        for(int j=0;j<=n;++j)
            dp[i+1][j]=(ll)(j*(j+1)/2+1)*dp[i][j]%mod,
            dp[i][j]=(ll)dp[i][j]*inv[j]%mod;
        for(int j=0;j<=M;++j)A[j]=0;
        for(int j=1;j<=n;++j)A[j]=inv[j+2];
        fft(dp[i],M,0),fft(A,M,0);
        for(int j=0;j<M;++j)A[j]=(ll)A[j]*dp[i][j]%mod;
        fft(A,M,1);
        for(int j=1;j<=n;++j)
            dp[i+1][j]=(dp[i+1][j]+(ll)A[j]*fac[j+2])%mod;
    }
    int ans=0;
//  for(int i=0;i<=n;++i)printf("[%d]",dp[m][i]);
    for(int i=0;i<=n;++i)ans=(ans+(ll)C(i,n)*dp[m][i])%mod;
    printf("%d",ans);
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值