AGC013D
题面
一开始有 \(n\) 个颜色为黑白的球,但不知道黑白色分别有多少,\(m\) 次操作,每次先拿出一个球,再放入黑白球各一个,再拿出一个球,最后拿出的球按顺序排列会形成一个颜色序列,求颜色序列有多少种。答案对 \(10^9+7\) 取模。
思路
首先,我们发现题目并没有给定初始状态,考虑枚举,一看 \(n\le 3000\),直接放弃尝试(在后面求解的转化中也会发现这没有用)。
然后再看到操作,考虑将一次完整的操作拆成三个部分,以操作次数为纵轴、排列中黑球的数量(指长度为 \(n\) 的球的“排列”)为横轴,如下图所示:
那么问题就转化为了寻找本质不同的折线(也就是不平行)的种数,为什么呢?显然,如果平行的话,操作就是一样的,那序列肯定相同(实在不理解可以自己画图)。而从同一种排列按照操作转移,可以统计出从一个点开始的所有情况并且不会重复。
于是,我们设计状态 \(f(i,j)\) 表示操作了 \(i\) 次,排列中有 \(j\) 个黑球。而处理平行的情况,就用到了等价类的思想,如果这条折线碰到了下界(黑球的数量为零),我们就将其计入答案。那么状态就改为 \(f(i,j,0/1)\) 表示当前第 \(i\) 次操作,有 \(j\) 个点,有没有碰到下界。最后 \(ans= \sum f(n,i,1)\)。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=3e3+1;
const int mod=1e9+7;
int n,m,ans;
int dp[maxn][maxn][2];//dp[i][j][1/0]表示第i次操作,有j个球,以及是否碰到过零
signed main()
{
std::ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;++i)dp[0][i][0]=1;
dp[0][0][1]=1;
for(int i=0;i<m;++i)//操作
{
for(int j=0;j<=n;++j)//黑棋数
{
dp[i][j][1]%=mod;
dp[i][j][0]%=mod;
if(j>=1)
{
if(j==1)//可以碰到下界
{
dp[i+1][j-1][1]+=dp[i][j][0];//拿走一个
dp[i+1][j][1]+=dp[i][j][0];//原封不动
}
else//碰不到一点
{
dp[i+1][j-1][0]+=dp[i][j][0];
dp[i+1][j][0]+=dp[i][j][0];
}
dp[i+1][j-1][1]+=dp[i][j][1];//以前
dp[i+1][j][1]+=dp[i][j][1];//碰到过
}
if(j+1<=n)//可以放一个
{
dp[i+1][j+1][1]+=dp[i][j][1];
dp[i+1][j][1]+=dp[i][j][1];
dp[i+1][j+1][0]+=dp[i][j][0];
dp[i+1][j][0]+=dp[i][j][0];
}
}
}
for(int i=0;i<=n;++i)
ans=(ans+dp[m][i][1])%mod;
cout<<ans<<endl;
return 0;
}
不定方程解数计算
题面
有不定方程 \(X_1,X_2+...+X_n=S\),每个量都是非负整数,对于每个 \(X_i\),有限制 \(X_i\le K\),计数这样的不定方程的解数(\(n\le 10^5,K\le10^5\))
思路
不好直接做,考虑容斥。枚举至少有几个大于 \(K\),\(S\) 变成 \(S-K\times i\),转化为没有限制的问题,然后就是插板法
CCPC2023秦皇岛热身赛B
鸽
CF1895F
鸽
AGC013E
鸽
CSA
题面
给定一棵树,求有多少个排列 \(p_1,p_2,p_3,...p_n\) 满足:\(p_1,p_2...p_n\) 的任意一个前缀 \(p_1,p _2...p_i\) 在树上是联通的
树的大小 \(n\le 10^6\)
思路
ARC101E
鸽
LIS
题面
求有多少个长度为 \(n\) 的序列,元素都是 \([1,m]\) 的整数,LIS 长度为 \(k\) (LIS 是严格的)
\(n\le 1000,m\le 10\)
CF1909F2
鸽
CF156D
鸽
AGC030D
鸽
NOI2015寿司晚宴
鸽
标签:10,数数,le,int,题面,简单,操作,dp From: https://www.cnblogs.com/PenguinChen/p/17999515