2024-10-23
传送门
A 简单dp
题意简述
在笛卡尔平面上有个点,初始时你在原点,然后每次只能走到自己的右上方,在开始前你可以把所有点绕着原点旋转某个角度,求能走的最多的点
容易发现一个性质是:任何一种合法的方案,任意相邻两个点的连线的斜率都是正的,把这些斜率中的最小值取出来,假设为
这就意味着,任何一种可能能成为答案的方案,一定存在某个
B
暂时不会
C
简单签到题,被 fyc 秒了,听说是什么放缩
D 简单莫反
题意简述
两个长度为的序列 ,求
大力推式子,需要用到前置知识:
首先为处理绝对值,按照
需要处理
于是有等式:令
然后将上式代入到答案中得到
然后将
后面那坨东西用两个数组来维护就行了, 具体的
sum1[n]
表示当前加入的
sum2[n]
表示当前加入的
然后
E 简单状压dp
把子序列自动机建出来,然后令
时间复杂度
F 有趣的树形dp
题意简述
给你一棵大小为的树,然后进行 次询问,每次询问 ,使得断掉若干个边,使得每个连通块的结构都是一个圆方树,且这些与这些连通块同构的园方树的割点的总和恰好为 ,求方案数
首先题意给了一段代码,你需要看出来这是在求广义圆方树,然后圆方树的性质就要求:
(1)若根节点的度数为
(2)然后把所有度数为
然后你就可以做一个树上背包了,具体的就是
然后这题 tmd 卡空间,我写了 3.5KB
代码才发现,草泥马
粘一下 MLE
代码
#include <bits/stdc++.h> #define mpr make_pair #define int long long #define pb push_back #define pii pair<int,int> #define st first #define nd second using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); for(;!isdigit(ch);ch=getchar())f^=ch=='-'; for(;isdigit(ch);ch=getchar())x=x*10+(ch^48); return f?x:-x; } mt19937 rnd(time(0)); const int mo=1e9+7; inline int qpow(int x,int t){ int ret=1; for(;t;t>>=1,x=x*x%mo)if(t&1)ret=ret*x%mo; return ret; } inline void red(int &x){x>=mo?x-=mo:0;} inline void chmin(int &x,int y){x=min(x,y);} inline void chmax(int &x,int y){x=max(x,y);} const int N=5e3+5; int dp[N][N][2][3],f[N][2][3],g[N][2][3],siz[N],dep[N],n,q; vector<int> edge[N]; void dfs(int u,int fa){ dep[u]=!dep[fa]; for(int v:edge[u])if(v!=fa)dfs(v,u); memset(f,0,sizeof(f)); f[0][0][0]=f[0][1][0]=1; for(int v:edge[u])if(v!=fa){ memset(g,0,sizeof(g)); for(int col=0;col<2;++col)for(int son=0;son<=2;++son){ for(int x=0;x<2;++x)for(int y=0;y<=2;++y){ for(int i=0;i<=siz[u];++i)for(int j=0;j<=siz[v];++j){ //不连接 if(y==1){ if(dep[v]==x) red(g[i+j][col][son]+=f[i][col][son]*dp[v][j][x][y]%mo); } else{ red(g[i+j][col][son]+=f[i][col][son]*dp[v][j][x][y]%mo); } //连接! if(x!=col)continue; int nxs=min(son+1,2ll),k=j+1; if(y==1){ if(dep[v]==x) red(g[i+k][col][nxs]+=f[i][col][son]*dp[v][j][x][y]%mo); else red(g[i+j][col][nxs]+=f[i][col][son]*dp[v][j][x][y]%mo); } else{ red(g[i+j][col][nxs]+=f[i][col][son]*dp[v][j][x][y]%mo); } } } } siz[u]+=siz[v]; memcpy(f,g,sizeof(f)); } for(int i=0;i<=siz[u];++i){ for(int son=1;son<=2;++son)for(int col=0;col<2;++col){ int rr=i; if(dep[u]==col&&col==2)++rr; red(dp[u][rr][col][son]+=f[i][col][son]); } red(dp[u][i][dep[u]][0]+=f[i][dep[u]][0]); } ++siz[u]; } void solve(){ n=read(),q=read(); for(int i=1;i<=n;++i){ memset(dp[i],0,sizeof(dp[i])); siz[i]=dep[i]=0; edge[i].clear(); } for(int i=1;i<n;++i){ int x=read(),y=read(); edge[x].pb(y); edge[y].pb(x); } dfs(1,0); // for(int i=1;i<=n;++i,puts(""),puts("")){ // printf("now print out the statement of node No.%lld\n",i); // for(int j=0;j<=siz[1];++j,puts(""))for(int x=0;x<=1;++x,puts(""))for(int y=0;y<=2;++y){ // printf("dp[%lld][%lld][%lld][%lld] = %lld , ",i,j,x,y,dp[i][j][x][y]); // } // } while(q--){ int t=read(); if(t>=siz[1])puts("0"); else{ int ans=0; for(int x=0;x<2;++x)for(int y=0;y<=2;++y){ if(y==1){ if(dep[1]==x) red(ans+=dp[1][t][x][y]); } else{ red(ans+=dp[1][t][x][y]); } } printf("%lld\n",ans); } } return; } signed main(){ // freopen("test.out","w",stdout); for(int cas=read();cas--;)solve(); return 0; }
G
H 简单dp
题意简述
给你一个串,然后你要求有多少个长度为 的字符串,满足 作为子串,不相互重叠出现在 中的次数恰好为
考虑对于一个串
具体的,你对于每个位置求出
然后每次一旦
然后就可以 dp 了,令 kmp
即可,时间复杂度是
I 傻逼模拟题
J 简单排列问题
题意简述
两个长度为排列 然后称一个自序列是好的,当且仅当 (1)这个子序列的值域连续 (2)这个子序列同时出现在 当中,求这个子序列个数
不难发现 (2) 的限制实际上比 (1) 好处理许多,从 (2) 出发来思考问题
而且一个排列的值域连续的子序列可以通过划定这个子序列的最大值和最小值来唯一确定,因此把这样一个子序列记为
不难发现性质:若
因此考虑双指针和 set
来维护,
记录
具体的,用两个 set
来维护 set
前驱后继是否分别相等,如果不等就把
K 简单最段路
题意简述
一张无向图,有个点,有 个边,边有权,点有权,你需要对于每个点 求出 ,然后走的过程中允许有一次不花费边权
比赛时唐了,想得太复杂了,其实只要建个超级汇点然后跑 dijkstra
即可
考虑分层图,两层图的情况是一致的,然后从一层到二层的边的权值为 0 ,然后二层的每一个点都向超级汇点连一个边权为
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· NetPad:一个.NET开源、跨平台的C#编辑器
· PowerShell开发游戏 · 打蜜蜂
· 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!