AGC021

大致上是从西安回来了。我去之前说的是“全程摆大烂”,实际上其实也差不多。正式开头了数分,也接触了一些复变函数和解析数论的东西。模拟赛基本摆烂,有一次还保龄 rating -125 掉下 1500。rating 还挂着的原因一半是因为不怎么打。

优越的环境似乎确实能够作为一种好的麻痹。曾经我也惯用这种方法。但是近日似乎发现并不奏效了:从昨天晚上上了高速就开始爆压。想了一些东西,但是自觉心智并不成熟(个人认为我的心理年龄还在小升初水平)就不去说道了。也许是因为哲学水平基本没有导致的,那么我觉得挺需要入门的。也许是因为门槛问题我没怎么学习相关。那么求教怎么入门。不过那大概是 NOI 以后的事情,那现在还有 25 天只能顶着高压强打。希望能给爆个美德吧。那只能稍微现实一点,放弃数学。

这些日子的资金流转很混乱,没准被坑了也说不定。但是能够确信的是 Kaguya 还没 vivo 50。

惊觉还有许多疑惑没能得到解答。还挺怀念和 joke3579 夜聊的日子的,也算是和理想的参照——传统意义上的“正常人”价值观的碰撞。但是也许每天留下半小时的自由学习时间是正当的。

割伤留下的痕迹被发现了,所幸没有多问。希望这样就过去了。但是以后不敢再用了,破伤风之刃。

理想乡北山群篇快完结了,十分的强大啊。莉莉白现在是眼睛被挖了的状态,感觉后边莉莉黑的时候能长回来。猜的。不过看地图妖之国在人之国南方啊,现在主角是从人之国出来往西北去雾都,再出来往北绕北山往东一直走,感觉是否暂时走不到。东边好像是竹林那一块?

推个歌:Se-U-Ra 的 F-Rozenette。

loj 是爆了吗?还是 ban 了?我怎么上不去。

鸡哥好像无了。乐。但是鸡你太美无了很不乐。感觉是昨天第二大的事。

Akira Complex 自杀了?????? 好像是真的。昨天晚上我九点回去然后过了不到十分钟就有消息。感觉是昨天最大的事。

[AGC021A] Digit Sum 2

要么是找一位减 \(1\) 后边全是 \(9\),要么是原来的数(不考虑会被全 \(9\) 干掉)。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <vector>
using namespace std;
char s[20];
int n;
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    int ret=0;
    for(int i=1;i<=n;i++)ret+=s[i]-'0';
    int i;
    for(i=1;i<=n;i++)if(s[i]!='9')break;
    if(i>1)i--;s[i]--;
    for(int j=i+1;j<=n;j++)s[j]='9';
    int ans=0;
    for(int i=1;i<=n;i++)ans+=s[i]-'0';
    ans=max(ans,ret);
    printf("%d\n",ans);
    return 0;
}

[AGC021B] Holes

面积无限,那么凸包内部的点就是 \(0\)。凸包上的点可以直接找中垂线分圆。不是很想拉计算几何板子。

盯了一下午发现输入是实数我写的 long long,很乐。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
const double eps=1e-8,pi=acos(-1);
struct node{
    double x,y;
    int id;
    node operator-(const node &s)const{return (node){x-s.x,y-s.y,id};}
    double operator^(node s){return x*s.y-y*s.x;}
    double len(){return x*x+y*y;}
}a[110],q[110];
int n,m;
double ans[110];
bool cmp(node a,node b){return fabs(a.x-b.x)<eps?a.y<b.y:a.x<b.x;}
signed main(){
    scanf("%lld",&n);
    if(n==1){
        puts("1");return 0;
    }
    for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y),a[i].id=i;
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;q[++m]=a[i++]){
        while(m>1&&(q[m-1]-q[m]^q[m-1]-a[i])<=0)m--;
    }
    int tmp=m;
    for(int i=n-1;i;q[++m]=a[i--]){
        while(m>tmp&&(q[m-1]-q[m]^q[m-1]-a[i])<=0)m--;
    }
    m--;q[0]=q[m];
    for(int i=1;i<=m;i++){
        double a=(q[i-1]-q[i]).len(),b=(q[i]-q[i+1]).len(),c=(q[i-1]-q[i+1]).len();
        double ret=acos(-1.0*(a+b-c)/sqrt(4*a*b));
        ans[q[i].id]=ret/(2*pi);
    }
    for(int i=1;i<=n;i++)printf("%.9lf\n",ans[i]);
    return 0;
}

[AGC021C] Tiling

两个相同的可以排成 \(2\times 2\) 的方块。不难发现这样最优,如果 \(n,m\) 有奇数那么把边角料塞一下就行了。但是有个特殊情况,\(n,m\) 都是奇数的话那么角上空一格的 \(3\times 3\) 可以各放两个。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <vector>
using namespace std;
int n,m,a,b;
char s[1010][1010];
int main(){
    scanf("%d%d%d%d",&n,&m,&a,&b);
    int A=a,B=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;i+=2){
            if(!a)break;
            s[n][i]='<';s[n][i+1]='>';a--;
        }
    }
    if(m&1){
        for(int i=1;i<n;i+=2){
            if(!b)break;
            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>=b){
                if(a)s[i][j]='<',s[i][j+1]='>',a--;
                if(a)s[i+1][j]='<',s[i+1][j+1]='>',a--;
            }
            else{
                if(b)s[i][j]='^',s[i+1][j]='v',b--;
                if(b)s[i][j+1]='^',s[i+1][j+1]='v',b--;
            }
        }
    }
    if((n&1)&&(m&1)&&n!=1&&m!=1){
        if(a||b){
            for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)s[i][j]='.';
            a=A,b=B;
            s[n-2][m-2]='<';s[n-2][m-1]='>';
            s[n-2][m]='^';s[n-1][m]='v';
            s[n-1][m-2]='^';s[n][m-2]='v';
            s[n][m-1]='<';s[n][m]='>';
            s[n-1][m-1]='.';
            a-=2,b-=2;
            for(int i=1;i<m-2;i+=2){
                if(!a)break;
                s[n][i]='<';s[n][i+1]='>';a--;
            }
            for(int i=1;i<n-2;i+=2){
                if(!b)break;
                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(s[i][j]!='.')continue;
                    if(a>=b){
                        if(a)s[i][j]='<',s[i][j+1]='>',a--;
                        if(a)s[i+1][j]='<',s[i+1][j+1]='>',a--;
                    }
                    else{
                        if(b)s[i][j]='^',s[i+1][j]='v',b--;
                        if(b)s[i][j+1]='^',s[i+1][j+1]='v',b--;
                    }
                }
            }
        }
    }
    if(a||b){
        puts("NO");return 0;
    }
    puts("YES");
    for(int i=1;i<=n;i++)printf("%s\n",s[i]+1);
    return 0;
}

[AGC021D] Reversed LCS

感觉被诈骗了。

题意等价于最长回文子序列的长度,于是区间 dp 即可。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
int n,K,dp[310][310][310];
char s[310];
int main(){
    scanf("%s%d",s+1,&K);n=strlen(s+1);
    for(int i=1;i<=n;i++)dp[i][i][0]=1;
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            for(int k=0;k<=K;k++){
                dp[i][j][k]=max(dp[i][j-1][k],dp[i+1][j][k]);
                if(s[i]==s[j])dp[i][j][k]=max(dp[i][j][k],dp[i+1][j-1][k]+2);
                else dp[i][j][k]=max(dp[i][j][k],max(dp[i+1][j-1][k],k?(dp[i+1][j-1][k-1]+2):0));
            }
        }
    }
    int ans=0;
    for(int i=0;i<=K;i++)ans=max(ans,dp[1][n][i]);
    printf("%d\n",ans);
    return 0;
}

[AGC021E] Ball Eat Chameleons

感觉被薄纱了。

首先还是考虑判定,容易发现只要红球不比蓝球少就行。这样一个球的序列我们要分成 \(n\) 组,考虑一个去重的方式:对于吃了红球比蓝球多不止一个的情况,把红球匀给别的。这样所有的就要么多一个要么一样多。

设红球个数 \(r\),蓝球个数 \(b\),那么对于 \(r=b\) 最后一个显然是蓝球,而且去掉不影响答案。那么不妨假设 \(r>b\)

然后考虑吃的红球和蓝球相等的,只给它们剩下一个红球和一个蓝球,别的全分给红比蓝多的。于是只要有 \(n-r+b\) 个红蓝子序列即合法。

然后考虑计数这个东西。不难发现相当于每个前缀的蓝球比红球至多多 \(b-n+r-b=r-n\) 个,是格路计数。

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <set>
#include <cstring>
#include <vector>
using namespace std;
const int mod=998244353;
int n,k,ans,jc[500010],inv[500010];
int C(int n,int m){
    if(n<m||m<0)return 0;
    return 1ll*jc[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
    scanf("%d%d",&n,&k);jc[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=k;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=k;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
    for(int i=(k>>1)+1;i<=k;i++)ans=(1ll*ans+C(k,i)-C(k,2*i-n+1)+mod)%mod;
    if(!(k&1))ans=(1ll*ans+C(k-1,k>>1)-C(k-1,k-n+1)+mod)%mod;
    if(k<n)ans=0;
    printf("%d\n",ans);
    return 0;
}

[AGC021F] Trinity

感觉比 E 简单。
首先闭着眼睛看都是按列转移。然后考虑每次确定一列。设 \(dp_{i,j}\) 为涂到 \(i\) 列,一共涂了 \(j\) 行的方案数(不考虑没涂的行),最后答案就是 \(\sum_{i=0}^n\binom nidp_{m,i}\)

转移。考虑上一次涂了 \(k\) 行,这次涂了 \(j\) 行,那么看第一个黑格和最后一个黑格有哪些是第这一列新涂的:

  1. 全是:直接随便在 \(j\) 行里边撒 \(k\) 行不去涂他们,剩下的全涂上,方案 \(\dbinom jk\)
  2. 有一个是:有一个要涂上,所以只撒 \(k-1\) 行,然后第一个和最后一个有 \(2\) 的贡献,方案 \(2\dbinom j{k-1}\)
  3. 两个都是:方案 \(\dbinom j{k-2}\)

一块加起来就是 \(\dbinom{j+2}k\)

然后还有 \(j=k\) 的特殊情况,只需要考虑占了几个格子,方案是 \(1+j+\dbinom j2\)。然后转移是卷积,直接卷就行了。

int n,m,dp[8010][210];
int main(){
    n=read();m=read();init(n+1<<1);
    for(int i=0;i<=n;i++)dp[i][1]=1;
    for(int i=2;i<=m;i++){
        poly a(n+1),b(n+1);
        for(int j=0;j<=n;j++)a[j]=1ll*Inv[j]*dp[j][i-1]%mod;
        for(int j=1;j<=n;j++)b[j]=Inv[j+2];
        a=a*b;
        for(int j=0;j<=n;j++){
            dp[j][i]=1ll*(j+1+(1ll*j*(j-1)>>1))*dp[j][i-1]%mod;
            dp[j][i]=(dp[j][i]+1ll*jc[j+2]*a[j])%mod;
        }
    }
    int ans=0;
    for(int i=0;i<=n;i++)ans=(ans+1ll*C(n,i)*dp[i][m])%mod;
    print(ans);putchar('\n');
    return 0;
}
posted @ 2023-06-27 19:31  gtm1514  阅读(21)  评论(0编辑  收藏  举报