隐藏页面特效

AtCoder Regular Contest 171

1|0Preface


小补一场ARC,D还是很有意思的但想了半天不会,鉴定为纯纯的彩笔


2|0A - No Attacking


考虑怎么放rook能使得留下来能放pawn的格子数量最多,手玩一下会发现先按照(2,2),(4,4),(6,6),的顺序放,放满后再接着放(1,1),(3,3),(5,5),是最优的

手玩一下可以得出在放了A个rook后最多能放的pawn的个数:

  • 2AN时,个数为(NA)×(A+N2A2)
  • 2A>N时,个数为(NA)2
#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; int t,n,a,b; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); for (scanf("%d",&t);t;--t) { scanf("%d%d%d",&n,&a,&b); int lim; if (a>n) { puts("No"); continue; } if (2*a<=n) lim=(n-a)*(a+(n-2*a+1)/2); else lim=(n-a)*(n-a); puts(b<=lim?"Yes":"No"); } return 0; }

3|0B - Chmax


原问题可以被转化为,对于每个点i,若i<pi则连一条ipi的有向边,否则不连边;最后要从每个点出发都能走到ai

首先考虑显然无解的情况,比较显然的时当有ai<i的元素出现时一定不合法

另外我们把所有ai相同的数看作一类,并将一类的数中下标最大的记为end,则所有end位置必须满足ai=i

接下来考虑计算方案数,不妨考虑从前往后确定每个数的pi取值数量,分类讨论:

  • i的后面还有和它一类的点j(即i不是end),则i只能连向j,此时方案数唯一
  • i的后面没有和它一类的点j(即iend),则i可以在下标i的点中选择一个没有入度的点作为pi

考虑第二种情况的方案数很容易计算,总复杂度O(n)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=200005,mod=998244353; int n,a[N],d[N],bkt[N],nxt[N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]),bkt[i]=n+1; for (i=1;i<=n;++i) if (a[i]<i) return puts("0"),0; for (i=n;i>=1;--i) nxt[i]=bkt[a[i]],bkt[a[i]]=i; for (i=1;i<=n;++i) if (nxt[i]>n&&a[i]!=i) return puts("0"),0; int ans=1,end=0; for (i=1;i<=n;++i) { d[i]+=d[i-1]; if (nxt[i]<=n) { ++d[nxt[i]]; continue; } ans=1LL*ans*(i-d[i]-end)%mod; ++end; } return printf("%d\n",ans),0; }

4|0C - Swap on Tree


很有意思的一个Counting题,算是那种不难但是需要想一下的类型

考虑两种方案本质相同的充要条件,首先很容易发现一个必要条件就是选择需要操作的边集要相同,这个手玩一下会发现很显然

但显然操作的顺序也会影响,不妨拿一个菊花图为例,不难发现如选择了中心相邻的x条边,则共有x!种本质不同的结果序列

考虑将这个结论推广,不难发现总方案数等于EEiVdegE,i!,即对于某个确定的选边方案,其贡献为每个点度数(只考虑选中的边)的阶乘

要求这个也很简单,不妨统一在向下的方向上统计贡献,设fi,0/1表示处理了i的子树,且i与其父亲的边是否要选中的贡献

对于每个点x,对其儿子yfy,0/1跑一个类似于背包的东西即可合并方案数,然后统计x的贡献时注意分是否要往上连讨论一下即可

总复杂度O(n2)

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=3005,mod=998244353; int n,x,y,f[N][2],fact[N]; vector <int> v[N]; inline void DFS(CI now=1,CI fa=0) { for (auto to:v[now]) if (to!=fa) DFS(to,now); RI i; int m=v[now].size(); static int g[N],tmp[N]; memset(g,0,sizeof(g)); g[0]=1; for (auto to:v[now]) if (to!=fa) { for (i=0;i<=m;++i) tmp[i]=g[i],g[i]=0; for (i=0;i<=m;++i) { (g[i]+=1LL*tmp[i]*f[to][0]%mod)%=mod; (g[i+1]+=1LL*tmp[i]*f[to][1]%mod)%=mod; } } for (i=0;i<=m;++i) { (f[now][0]+=1LL*g[i]*fact[i]%mod)%=mod; (f[now][1]+=1LL*g[i]*fact[i+1]%mod)%=mod; } } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i; for (scanf("%d",&n),i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x); for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod; return DFS(),printf("%d",f[1][0]),0; }

5|0D - Rolling Hash


感觉不难的一个题,但我犯病了想着用前缀和算Hash值的时候被常见的形式固定死了,导致没法转化出关键一步直接GG

首先由于题目中给一个串的Hash值定义和我们熟悉的字符串Hash完全相同,因此考虑设Hash函数的前缀和数组为hi,则hash(l,r)=hrhl1×Brl+1,由于式子中含有Brl+1很难处理

其实这就完全陷入了陷阱之中,我们不妨稍微修改下Hash函数前缀和的定义,直接令hi=(j=1ixjBnj)modP(注意和传统的定义的区别就是直接把每一个位置的位权定死)

这样做的好处就是可以发现此时hash(l,r)=(hrhl1)×1Bnr,而hash(l,r)0的充要条件就变成hl1hr了,这个条件就非常好

不难发现当确定了{hi}后自然就确定了{xi},而且二者可以相互转化,因此只需要判断是否有合法的{hi}序列即可

不妨把一个限制(l,r)看作一条l1r的双向边,则题目等价于判定能否用P种颜色给某个图染色,使得任意相邻两点的颜色不同

这个问题有个经典的DP做法,设fmask表示将点集为mask及其导出子图染色所需的最小颜色数,每次转移的时候枚举mask的子集S,若S是独立集则转移fmaskS+1fmask

总复杂度O(3n),应该可以用类似sosdp或者FMT之类的科技优化到O(2n×nO(1)),但直接上枚举子集已经足以通过此题

#include<cstdio> #include<iostream> #include<utility> #include<vector> #include<cstring> #include<cmath> #include<cstdlib> #include<algorithm> #include<queue> #include<set> #include<map> #include<set> #include<array> #include<random> #include<bitset> #include<ctime> #include<limits.h> #include<assert.h> #include<unordered_set> #include<unordered_map> #define RI register int #define CI const int& #define mp make_pair #define fi first #define se second #define Tp template <typename T> using namespace std; typedef long long LL; typedef long double LDB; typedef unsigned long long u64; typedef __int128 i128; typedef pair <int,int> pi; typedef vector <int> VI; typedef array <int,3> tri; const int N=17; int P,B,n,m,l,r,E[N][N],g[1<<N],f[1<<N]; int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); RI i,j,k; scanf("%d%d%d%d",&P,&B,&n,&m); for (i=1;i<=m;++i) scanf("%d%d",&l,&r),E[l-1][r]=E[r][l-1]=1; for (i=1;i<(1<<n+1);++i) for (g[i]=1,j=0;j<=n;++j) if ((i>>j)&1) for (k=j+1;k<=n;++k) if ((i>>k)&1) if (E[j][k]) g[i]=0; for (i=1;i<(1<<n+1);++i) for (f[i]=1e9,j=i;j;j=(j-1)&i) if (g[j]) f[i]=min(f[i],f[i^j]+1); return puts(f[(1<<n+1)-1]<=P?"Yes":"No"),0; }

6|0Postscript


最近慢慢要开始准备给新一届的暑假前集训搬题了,可能能补题的时间会变少一些,但还是尽量能补就补吧


__EOF__

本文作者hl666
本文链接https://www.cnblogs.com/cjjsb/p/18059090.html
关于博主:复活的ACM新生,目前爱好仅剩Gal/HBR/雀魂/单机/OSU
版权声明:转载请注明出处
声援博主:欢迎加QQ:2649020702来DD我
posted @   空気力学の詩  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示