Competition Set - AtCoder I

这里记录的是这个账号的比赛情况。

ARC177

2024-5-12

Solved:5/6

2370->2488

D(Easy+,2299)

一条直线上有 n 根电线杆,高度均为 h,编号为 i 的位于坐标 xi(不保证 x 单调)。现在发生了 n 次地震,第 i 次编号为 i 的电线杆会倒下(如果此前没有倒下),等概率倒向左侧或右侧。如果倒下之后碰到了其他未倒下的电线杆,那么会这根电线杆会倒向同一个方向。对 i=1,2,,n,求恰好第 i 次地震后所有电线杆倒下的概率。

Solution:所有电线杆高度相同是一个很好的性质。这意味着我们可以把所有电线杆分为若干组,组与组之间互相独立。对于同一组,首先最小位置倒下,带倒一侧,然后就可以递归到另一侧处理。这样可以计算出每一组全部倒下的时间的分布列。然后考虑计算 i 次内全部倒下的概率,那就只要记录每一组倒下的概率,拿一个线段树维护全局乘积就可以了。复杂度 O(nlogn)

E(Medium-,3227)

n 个人参加一场 OI 比赛。比赛有 5 个题,编号为 1,2,3,4,5,每道题分数都是正整数,按照编号单调不减,且没有部分分。最终的排名先按照分数从高到低排序,再按照罚时从低到高排序(假设没有两人分数和罚时都相同)。一位记者记录下了每个人通过的题目以及他们最终的排名 r1rnri 表示 i 的排名),然而他发现排名可能记错了。现在问,对所有可能的题目分数分布与罚时分布,实际排名 d1dn 与记录下的排名之差的平方和 i=1n(diri)2 的最小值。

Solution:本质不同的过题情况只有 32 种。考虑这 32 种情况之间的分数顺序,打个表可以发现有 4672 种。那么对固定的顺序,实际的排名就是先按照情况排序,然后每一种情况内部随便排(罚时)。但是这些顺序中,有一些相等的情况,而把相等的情况拆开的顺序是不用考虑的。再打个表就只剩下 113 种不同的顺序了。为了最小化目标式,利用排序不等式可以知道每一种情况内部和 r 同序。可以预处理成 kx+b 的形式。然后枚举所有情况更新答案就可以了。

(不是,哥们,这题真简单吧)

ARC172

2024-2-18

Solved:4/6

2310->2370

D(Hard-,2936)

给定所有数对 (i,j),1i<jn 的一个排列 (a1,b1),(a2,b2),,(am,bm),m=12n(n1)。请在 n 维空间中构造 n 个点 p1pn,使得 d(pa1,pb1)<d(pa2,pb2)<<d(pam,pbm),这里 d 表示两点间的欧几里得距离。

Solution:(搬运题解)首先直接给出构造:设 ai<bi,rai,bi=mi+1。则

p1=(108,r12,r13,,r1n)p2=(0,108,r23,,r2n)pn=(0,0,,0,108)

是一个合法的构造。

这个构造有点天才。可以这样想:先构造 n 个点两两间距离相同,那么 pi 只有第 i 维是 1,其他维度是 0 即可。再做一些微调,设 pi 的第 j 个维度比原本多了一个极小值 ai,j,则 pi,pj 间的距离可以视为

2(ai,j+aj,i)+(ai,1aj,1+ai,2aj,2++ai,naj,n)

后面部分是二阶小量,有忽略掉,那就视为 2(ai,j+aj,i)。这样只要 ai,j+aj,i 的顺序是给出的排列的逆序即可。

E(Medium-,2358)

求一个 n,使得 nn 的末 9 位是 x。保证 x10 互质。

Solution:一位一位填即可,为保证 x 的末位需要确定 nmod20,接下来每次都只需要把模数乘 10,直接模拟即可。

ARC171

2024-2-4

Solved:2/6

2332->2310

ARC169

2023-12-11

Solved:3/6

2296->2332

ARC165

2023-9-17

Solved:3/6

2290->2296

ARC164

2023-7-9

Solved:4/6

2295->2290

ARC161

2023-5-28

Solved:4/6

2203->2295

D(Easy,1379)

定义一个图的密度为边数与点数之比。问是否存在一个点数n,边数nd的图,其任意真子图的密度小于d

Solution:猜答案。凭感觉。没有证。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m;
int main(){
scanf("%d%d",&n,&m);
if(m*2+1>n){
printf("No\n");
return 0;
}
printf("Yes\n");
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
printf("%d %d\n",i,(i+j-1)%n+1);
return 0;
}

AGC062

2023-5-21

Solved:1/6

2216->2203

ARC157

2023-2-25

Solved:4/6

2189->2216

C(Medium,1802)

给定一个XY矩阵,一条左上角到右下角的路径的分值定义为路径上连续两个Y的组数。求所有可能路径的分值的平方和。

Solution:经典DP。递推两个量,一个是到(i,j)所有路径的分值和fi,j,一个是平方和dpi,j。递推式为

fi,j=fi1,j+fi,j1+C(i+j3,i2)+C(i+j3,j2)

dpi,j=dpi1,j+dpi,j1+(2×fi,j1+C(i+j3,i2))+(2×fi1,j+C(i+j3,j2))

其中倒数第二项在ai,j==ai1,j==Y是有效,倒数第一项在ai,j==ai,j1==Y时有效。

D(Medium+,2435)

给定一个XY矩阵,用横线和竖线将矩阵分为若干块,使得每块内恰有2个Y。求方案数。

Solution:说到底是暴力。先求二维前缀和,然后枚举第一条横线,它以上的Y的数目是总数的约数。接着求出后面每条横线的可能位置。再枚举竖线,要求在每条横线的位置的值一定。最后求积就得到了这个横线位置的解数。细节很多。

AGC060

2022-12-25

Solved:3/6

Rank47!

2031->2189

A(Easy+,1345)

补全一个字符串,使得其任意一个子串中不存在一个字母出现次数超过一半。

Solution:容易分析出只需要对长为3的子串满足,然后DP。

B(Medium+,1996)

n×m 的方格表中填入一些不超过 2k1 的数。考虑所有从左上角到右下角的最短路径,要求其中满足路径上数异或和为 0 的路径只有给定的 S 一条,问是否有解。

Solution

C(Hard,2680)

称一个长为 2n1 的排列 P 像堆,如果 Pi<P2i,且 Pi<P2i+1。给定 a,b,u=2a,v=2b+11,在所有像堆的排列中任取一个,求 Pu<Pv 的概率。

Solution

组合计数还是好做

ABC264

2022-8-13

Solved:7/8

Rank79!

Unrated

E(Medium-,1229)

给定n+m个点的无向图,其中后m个点为源点。给定q次删边操作,求每次操作后与源点联通的非源点数。

Solution:其实是套路题。倒序处理,化删边为连边,并查集维护,合并时尽可能向后。

F(Medium+.1878)

给定01矩阵及翻转各行各列的代价,求使得从左上角到右下角有同色通路的最小代价,这里通路只能向右或向下。

Solution:考虑DP,用dp[i][j][p][q]表示从左上角到达(i,j)的最小代价,且此时第i行反转了p次,第j列反转了q次。显然只用考虑p,q{0,1}

每个位置可以从上方或左方转移而来,必须有两个位置相同才能转移。计算代价时只考虑所扩展的行(列)。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
int h,w,r[N],c[N],a[N][N];
char s[N];
ll dp[N][N][2][2];
int main(){
scanf("%d%d",&h,&w);
for(int i=1;i<=h;i++)scanf("%d",r+i);
for(int i=1;i<=w;i++)scanf("%d",c+i);
for(int i=1;i<=h;i++){
scanf("%s",s+1);
for(int j=1;j<=w;j++)
a[i][j]=s[j]-'0';
}
memset(dp,0x3f,sizeof(dp));
dp[1][1][0][0]=0;
dp[1][1][1][0]=r[1];
dp[1][1][1][1]=r[1]+c[1];
dp[1][1][0][1]=c[1];
for(int i=1;i<=h;i++)
for(int j=1;j<=w;j++)
for(int st=0;st<=15;st++){
int p1=st&1,p2=(st>>1)&1,q1=(st>>2)&1,q2=(st>>3)&1;
if(i!=1&&(a[i][j]^p2^q2)==(a[i-1][j]^p1^q1)&&q1==q2)
dp[i][j][p2][q2]=min(dp[i][j][p2][q2],
dp[i-1][j][p1][q1]+r[i]*p2);
if(j!=1&&(a[i][j]^p2^q2)==(a[i][j-1]^p1^q1)&&p1==p2)
dp[i][j][p2][q2]=min(dp[i][j][p2][q2],
dp[i][j-1][p1][q1]+c[j]*q2);
}
printf("%lld\n",min(min(dp[h][w][0][0],dp[h][w][0][1]),
min(dp[h][w][1][0],dp[h][w][1][1])));
return 0;
}
G(Medium,2348)

给出n个长度不超过3的字符串,每个字符串有一个代价。对于一个字符串,定义其价值为给出的字符串在其中出现的次数与代价的乘积之和。对所有字符串,求价值最大值,或判断不存在。

Solution:还是比较显然的吧。

直接建图,每个结点是一个长为2的字符串,在两结点AB与BC间连有向边,边权为C,BC,ABC的代价之和。另建一个源点,向每个结点AB连边,代价为A,B,AB的代价之和。

从源点出发跑单源最长路,若有正环就无解。SPFA没死。

(大不合理,F的DP显然比G的建图难想)

ARC145

2022-7-30

Solved:4/6

1973->2031

A(Medium-,596)

给定长为nAB字符串,可以将相邻两个改为AB,问是否能将原字符串变为回文串。

Solution:分类讨论题还不难首先特讨n=2。然后:

若开头为B,可以将中间全部改为A,末尾改为B

若开头不为B,结尾为B,无解;

若开头,结尾均为A,可以将第2个和第n1个改为B,中间全为A

B(Easy,767)

两人取石子,先手取A的正数倍,后手取B的整数倍,这里A,B给定。求1,2,...,n中使先手获胜的初始石子数的个数。

Solution:策略显然:先手往死里取。若AB,只要先手能取就必胜;否则,先手取完后少于B个即获胜。

C(Easy+,1697)

对于一个1,2,...,2n的排列,定义其分数为:将排列划分为两个等长的子序列,对应项乘积和的最大值。给定n,求使得分数取最大值的排列数。

Solution:调整法易得最大值在1×2+3×4+...+(2n1)×2n时取到,只用考虑每一对数的位置。选定左侧,则右侧必须顺次排列,转化一下就是卡特兰数。故答案为(2n)!(n+1)!×2n

D(Medium+,2297)

构造满足以下条件的集合:集合中含n[107,107]的整数,其和为m,且不含等差子列。

Solution:FVMO牛逼!

MO原题:求证:可以找出1983个不超过105的正整数,其中不含等差子列。

原题证明:取三进制下只含数码0,1的数即可。

本题思路类似,前n1个顺次取这样的数,最后一个大于前一个的两倍,然后使得总和模nm同余,最后补上一个偏移量。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
typedef long long ll;
ll n,m,cnt,sum,ans[N],del;
bool check(ll x){
while(x){
if(x%3==2)return false;
x/=3;
}
return true;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;cnt<n-1;i++)
if(check(i)){ans[++cnt]=i;sum+=i;}
ll r=(m-sum)%n;
ans[n]=(ans[n-1]*3+n)/n*n+r;
del=(m-ans[n]-sum)/n;
for(int i=1;i<=n;i++)
printf("%lld\n",ans[i]+del);
return 0;
}

ABC260

2022-7-17

Solved:7/8

Rank98!

1908->1973

E(Medium-,1692)

给定n个二元组,值域为[1,m],求长度为k,包含每个二元组中至少一个数的区间数目,这里k需取遍1,...,m

Solution:考虑每个左端点,求出使其满足条件的最小右端点,用双指针。双指针时有一个方法:将所有数塞到一个数组中,维护出现的二元组数目。

[l,r]可行,则[l,t],rtm可行,从而差分。最后求一次前缀和。

F(Medium,1995)

给定一个二分图,求其中是否有长为4的环。特殊限制:右部点数t3000

Solution:对每个右部点,从它出发走两步,如果两次走到同一个点则得到解。若没有走到,则每次时间为O(t),总时间不超过O(t2)

G(Medium+,2339)

给定一个n×n 的OX矩阵和m,给出q个询问(x,y),求满足sxn,tyn,(xs)+yt2<m且填O的点(s,t)数目。

Solution:一个裸的模板题:二维斜向差分,和P8228类似。首先对斜率为12方向差分,对这个差分数组,再从横向和斜率12方向差分。对每个O转化一下,再求前缀和就可以了。(为什么又是差分)

说着不难,实际上还是有一些细节要处理的。

ABC258

2022-7-2

Solved:6/8

1864->1908

E(Medium-,1545)

给定周期为n的序列的一个周期,从第一个开始加和,和超过x就计一段。求第k段的长。

Solution:考虑从每个位置出发会到达哪里。用双指针。再由此建立段开头的周期即可。

G(Medium,1944)

给定一张图,求三角形数目。

Solution:枚举一条边,计算其两个端点所连点的集合之交的大小。用 bitset

ARC142

2022-6-19

Solved:3/6

1880->1864

B(Medium+,692)

构造 n×n 的矩阵,填入从 1 开始的连续整数,使得对任意一格,其周围(八连通)比它大的个数和比它小的个数不相同。

Solution:首先,边界的格子有奇数个邻格,不用考虑。

其次,如果能保证每个格周围有至少 5 个格与其大小关系相同,就解决了题目。由此可以想到按照圈来填,外圈所填一定大于内圈。

如果 n 为奇数,直接让每一圈大小相间即可。如果 n 为偶数,先让角落处为该圈最大,然后让边角处连续,其它地方大小相间。倒数第二圈单独构造,可以完成。

题解的做法简单:按行考虑,每行大小交替,各行递减。

C(Medium,1347)

n 个点的树,可以询问除 12 之外任意两点间距离,要求用不超过 2n 次询问确定 12 的距离。

Solution:先找出所有与 1 相距 1 的点。若一个没有,则答案为 1。若至少两个,询问这两个与 2 的距离即可判断。

若恰有一个,询问这个与 2 的距离。若不为 2,则 1 为叶子结点,直接确定。否则,对 2 重复上述操作,假如也没有确定,询问两次得到的点的间距即可。

ABC256

2022-6-18

Solved:6/8

1839->1880

ABC251

2022-5-14

Solved:6/8

1816->1839

D(Easy+,1463)

构造长不超过300的数列,使得1106中的整数都可以从该数列中取出不超过三个做和得到。

Solution:需要一点想法。我的做法是按照进制的思想,数列包含1,2,...,99,100,200,...,9900,10000,20000,...,990000.

F(Easy+,1703)

求给出的图中两棵生成树,其一满足以1为根时,图中非树边的端点有祖先关系,另一恰好相反。

Solution:第一棵树最直接的情况是一条链,所以尝试构造链,贪心地连边。也就是直接dfs,看到未加入生成树的点就连边。

第二棵树最直接的情况是一个菊花,所以dfs时遇到一个点就把它所有的边都加入生成树。

ABC242

2022-3-5

Solved:7/8

Rank89!(第一次进前100)

1711->1816

D(Easy,1286)

给定字符串S,只包含A,B,C,一次变换将A变为BCB变为CAC变为AB。求t次变换后第l个位置的字符。多组询问,tl long long范围内。

Solution:还是挺有意思的。

可以在模3意义下考虑。每一个位置变为它加1和它加2。二进制转一下就搞完了。

E(Easy,1365)

给定整数n及长为n的字符串S,求满足XS的回文串X个数。

Solution:按位考虑,前[n+12]位直接贪心,然后特判前一半全部一样的情况。

F(Medium+,2268)

n×m的棋盘上放置b个黑車和w个白車,求使得黑白之间互不攻击的放法数。

Solution:先考虑黑棋,设k个黑棋占据ij列的方案数为dp[k][i][j]。这里把k作为阶段进行递推。

状态转移方程为

dp[k][i][j]=1k((dp[k1][i1][j1]+dp[k1][i1][j]+dp[k1][i][j1])×i×j+dp[k1][i][j]×(i×jk+1))

然后枚举黑棋占领的行列数,剩下的行列都可以放白棋。

预处理阶乘,阶乘逆元,逆元等。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353,N=55,M=2505;
int n,m,b,w,fac[M],fac_inv[M],inv[M],dp[M][N][N],ans;
int power(int a,int b){
int c=1;
for(;b;b>>=1){
if(b&1)c=1ll*c*a%mod;
a=1ll*a*a%mod;
}
return c;
}
int C(int n,int m){
if(n<m||n<0||m<0)return 0;
return 1ll*fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;
}
int main(){
fac[0]=1;
for(int i=1;i<=M-5;i++)fac[i]=1ll*i*fac[i-1]%mod;
for(int i=0;i<=M-5;i++)fac_inv[i]=power(fac[i],mod-2);
for(int i=1;i<=M-5;i++)inv[i]=power(i,mod-2);
scanf("%d%d%d%d",&n,&m,&b,&w);
dp[1][1][1]=1;
for(int k=2;k<=n*m;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
//if(i*j<=k)continue;
dp[k][i][j]=(1ll*dp[k-1][i-1][j-1]*i*j%mod+1ll*dp[k-1][i-1][j]*i*j%mod+
1ll*dp[k-1][i][j-1]*i*j%mod+1ll*dp[k-1][i][j]*(i*j-k+1)%mod)*inv[k]%mod;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
ans=(ans+1ll*dp[b][i][j]*C(n,i)%mod*C(m,j)%mod*C((n-i)*(m-j),w)%mod)%mod;
printf("%d\n",ans);
return 0;
}

(闲话:该代码正好1024Byte)

G(Medium+,1828)

给出长为n的数组,求[l,r]中的数能配成几对。多组询问。

Solution:没想出什么常规做法,于是写了莫队。

(其实是一开始把题目看成了P1494)

ABC240

2022-02-20

Solved:6/8

1691->1711

F(Easy+,1589)

xiyi的形式给出长为m的数列C,数列B满足Bi=k=1iCk,数列A满足Ai=k=1iBk,求A中的最大项。

Solution:注意到B的每一段是单调的,那么可以找出每一段中正负交界出的点,考虑这些位置的A值即可。

G(Hard,2462)

(0,0,0)出发,每一步沿坐标轴方向移动一个单位长度,求用n步走到(x,y,z)的方案数。

Solution:(搬运题解)

一维的情况非常简单,但这是此后的基础。记n步走到x的方案数为f1(n,x)

二维需要一点想法:将(x,y)变换为(x+y,xy),那么移动就是从(x,y)(x+1,y+1),(x1,y+1),(x+1,y1),(x1,y1),这时就可以将横纵分开考虑,方案数为f2(n,x,y)=f1(n,x+y)×f1(n,xy)

三维时,枚举一下z方向就可以了。

ABC237

2022-01-30

Solved:6/8

1669->1691

F(Medium-,1857)

求长为n,值域[1,m]LIS长为3的整数列数目。

Solution:把LIS问题O(nlogn)算法中的另一个数组记录在状态中,直接推就可以了。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,dp[1005][12][12][12],ans;
int main(){
scanf("%d%d",&n,&m);
dp[0][m+1][m+1][m+1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m+1;j++)
for(int k=1;k<=m+1;k++)
for(int l=1;l<=m+1;l++){
if(j>k||k>l||j>l)continue;
if(j==k&&k!=m+1)continue;
if(k==l&&k!=m+1)continue;
for(int c=1;c<=m;c++){
if(c<=j)dp[i][c][k][l]=(dp[i][c][k][l]+dp[i-1][j][k][l])%mod;
else if(c<=k)dp[i][j][c][l]=(dp[i][j][c][l]+dp[i-1][j][k][l])%mod;
else if(c<=l)dp[i][j][k][c]=(dp[i][j][k][c]+dp[i-1][j][k][l])%mod;
}
}
for(int j=1;j<=m;j++)
for(int k=j+1;k<=m;k++)
for(int l=k+1;l<=m;l++){
ans=(ans+dp[n][j][k][l])%mod;
}
printf("%d\n",ans);
return 0;
}

ARC134

2022-01-29

Solved:3/6

1646->1669

ABC234

2022-01-08

Solved:6/8

1636->1646

F(Medium-,1596)

给定字符串S,求由S中字符的子集组成的字符串数。

Solution:用dp[i][j]表示用前i个字母组成长为j的字符串的方案数。

条件相当于是对每种字符使用次数做出了限制。

状态转移方程为

dp[i][j]=k=0min(cnti,j)dp[i1][jk]k!

直接推就好了。

G(Hard-,2306)

给定一个数列a,将其分成若干段,求所有分法各段极差的积之和。

Solution:用dp[i]表示以i结尾的所有分法各段极差的积之和。

状态转移方程为

dp[i]=j=0i1dp[j]×(max(j+1...i)min(j+1...i))

这样会T飞起来。考虑一下怎么优化它。

大致思路如下:先把乘积式拆成两块,以最大值为例。j+1...i的最大值就是max(max(j+1...i1),i),加上最大值数组的单调性,可以使用一个栈去维护。每一次更新最大值序列时,该段的dp值所乘的系数加上同一个值,这样就可以用前缀和优化。那么就做到了线性复杂度。

这题的确是挺难的,赛时没做出来。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5,mod=998244353,INF=1<<30;
struct rec{int l,r,val;};
int n,a[N],dp[N],s[N],sum,tmp,maxn,minn=INF;
stack<rec> MX,MN;
int main(){
scanf("%d",&n);
for(int i=2;i<=n+1;i++)scanf("%d",a+i);
maxn=minn=a[2];
s[1]=dp[1]=1;
for(int i=2;i<=n+1;i++){
maxn=max(maxn,a[i]);minn=min(minn,a[i]);
tmp=i-1;
while(!MX.empty()&&MX.top().val<=a[i]){
rec tt=MX.top();tmp=tt.l;
sum=(sum+1ll*(s[tt.r]-s[tt.l-1]+mod)%mod*(a[i]-tt.val))%mod;
MX.pop();
}
MX.push(rec{tmp,i-1,a[i]});
tmp=i-1;
while(!MN.empty()&&MN.top().val>=a[i]){
rec tt=MN.top();tmp=tt.l;
sum=(sum+1ll*(s[tt.r]-s[tt.l-1]+mod)%mod*(tt.val-a[i]))%mod;
MN.pop();
}
MN.push(rec{tmp,i-1,a[i]});
dp[i]=sum;
s[i]=(s[i-1]+dp[i])%mod;
}
printf("%d\n",dp[n+1]);
return 0;
}

ABC233

2021-12-25

Solved:5/8

1677->1636

AGC056

2021-12-04

Solved:1/6

1589->1677

ABC230

2021-12-03

Solved:6/8

1406->1589

F(Medium+,2113)

给定数列a,可以将相邻两项求和合并,求若干次操作后可能的结果数。

Solution:相当于在前缀和数列中删数。而前缀和数列与原数列是一一对应的。

于是就变成了只用求一个数列的不同子序列数。

使用动态规划,令dp[i]表示以第i个数开头的不同子序列数。

倒序扫描,遇到一个数时看一下它下一次在哪里出现,只用对两个位置之间的数在前面加前缀。

实时维护后缀和即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,mod=998244353;
ll n,a,s[N],dp[N],b[N];
map<ll,vector<ll> > M;
map<ll,ll> pos;
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a);
s[i]=s[i-1]+a;
if(i!=n)M[s[i]].push_back(i);
}
for(int i=1;i<n;i++)pos[s[i]]++;
b[n-1]=dp[n-1]=1;pos[s[n-1]]--;
for(int i=n-2;i>=1;i--){
int x=--pos[s[i]],y=n-1,f=1;
if(x+1!=M[s[i]].size())y=M[s[i]][x+1],f=0;
dp[i]=(b[i+1]-b[y+1]+f+mod)%mod;
b[i]=(b[i+1]+dp[i])%mod;
}
printf("%lld\n",(b[1]+1)%mod);
return 0;
}

(麻了,好神仙啊,我怎么想出来的)

ABC228

2021-11-20

Solved:4/8

1379->1406

ABC226

2021-11-07

Solved:6/8

1198->1379

F(Medium,2086)

一个1...n的排列(p1,p2,...,pn)的分数定义为:

开始时,第i个人拿着i号球。每一次操作,第i个人把手中的球传给第pi个人。则分数就是所有人重新拿到自己的球的操作次数。

1...n所有排列的分数的k次幂之和。

Solution:首先要明确所谓的“分数”。

对于一个排列(p1,p2,...,pn),构建一张图,从ipi连边。最后图中会形成若干个环,所有环长的最小公倍数就是这个排列的分数。这一点通过感性理解不难发现。

此时就可以考虑将n分拆为若干个数代表环长。注意到n50,所以可以执行一个dfs,从大到小分拆即可。

然后就是一些细节,比如平均分配,具体不细讲了。

G(Hard-,2373)

五种物品五种人,物品重量分1,2,3,4,5,人的力量分1,2,3,4,5,当人的力量不小于物品重量和时,人可以搬起这些物品。给定各种人数和物品数,问能否搬完。

Solution:(搬运题解)

贪心。先让人5搬物5,再让人4,5搬物4,然后让人3,4,5搬物3。接下来能搬2的人都再搬一个2。最后把1处理掉就可以了。

贪心的正确性证明比较麻烦,可以感性理解一下:1不用考虑,最后能放哪儿放哪儿;3,4,5每个人至多搬1个。于是就应该这么做。

还是挺需要想法的。

ABC225

2021-10-30

900->1198

Solved:5/8

E(Easy+,1678)

选择n个7中的一些放入坐标系内,要求从原点能看到所有选择的7。求最大数目。

Solution:就是区间合并,考虑每个7占用的斜率区间即可。

F(Hard,2612)

n个字符串中选择k个首尾相连,求字典序最小的方案。

Solution:(搬运题解)

将给出的字符串记为S1...Sn,按照Si+Sj<=Sj+Si排序。

然后进行DP:用dp[i][j]表示从Si...Sn中选出j个的最小字典序。则状态转移方程为:

dp[i][j]=min(dp[i+1][j],dp[i+1][j1]+Si)

此处省略一系列正确性证明。

ABC219

2021-09-18

944->900

Solved:3/8

ABC216

2021-08-29

317->944

Solved:7/8

F(Medium-,1541)

给定数列{an},{bn},求集合S1,2,...,n 的个数,使得 maxiSaiiSbi

Solution:先将a排序,从小到大枚举maxiSai。设这是排序后第k个。

假设已经确定了这个值,那么问题变为在b1,b2,...,bk中选出若干个,其和不超过maxiSai。这只需要记忆化搜索就可以了。

G(Medium-,1963)

构造01数列,使得在给定区间内至少有给定数目的1,且1的个数最少。

Solution:对于每一个区间,将需要安放的1尽可能向后放,用贪心算法容易知道这是正确的。

详言之,先将所有区间以右端点为第一关键字排序,用树状数组维护01序列,依次扫描每个区间,算出它还需要几个1,全部放在最末尾即可。

ABC215

2021-08-21

24->317

Solved:4/8

ABC204

2021-06-06

0->24

Solved:2/6

posted @   by_chance  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示