咋是计数专场啊,讨厌计数!
就会一个签到题,恼了。
rank 21,T1 100pts,T2 0pts,T3 20pts,T4 0pts
dp设计状态不行。
T3典型的背包没看出来,T2简单dp不会设计状态。
有一些套路还是要学
好数(number)
签到题。
假设一个数\(a_i\)是好数,那么一定有\(a_i=a_x+a_y+a_z(x\le y\le z)\)
用一个bitset维护\(a_x+a_y\)是否出现,对于每一个数\(a_i\),查询是否存在一个数\(z\),使得\(a_i-a_z\)出现过即可。
时间复杂度\(O(n^2)\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("number.in"),*OutFile = outfile("number.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 5e3 + 10,V = 2e5;
int n,a[N],ans = 0;
bitset<V*2+10> pd;
inline void solve(){
cin>>n;rep(i,1,n,1) cin>>a[i];
rep(i,1,n,1){
rep(j,1,i-1,1) if(pd[a[i] - a[j] + V]){ans++;break;}
rep(j,1,i,1) pd.set(a[i] + a[j] + V);
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
SOS字符串(sos)
dp,记\(f_{i,j,k}\)表示前\(i-1\)个字符中已经出现了\(k\)个SOS,并且现在匹配到了SOS的第\(j(j\in\{0,1,2\})\)个字符。这样设计是\(O(n^2)\)的,但是由于\(k>3\)没有用,所以直接将\(k>3\)的转移到\(k=3\)即可。
\[f_{i,j,k}+=\begin{cases} f_{i-1,0,k}+f_{i-1,1,k}&j=1\\ f_{i-1,1,k}&j=2\\ f_{i-1,2,k-1}&j=0,k>0\\ f_{i-1,2,k}&j=0,k=3\\ 25\times f_{i-1,0,k}+24\times f_{i-1,1,k}+25\times f_{i-1,2,k}&j=0 \end{cases}\]时间复杂度\(O(n)\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
FILE *ErrFile = errfile("err.err");
#else
FILE *Infile = infile("sos.in"),*OutFile = outfile("sos.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10,mod = 1e9+7;
ll n,f[N][4][4],ans;
inline void solve(){
cin>>n;
f[0][0][0] = 1;
rep(i,1,n,1) rep(k,0,3,1){
f[i][1][k] += f[i-1][1][k] + f[i-1][0][k];
f[i][2][k] += f[i-1][1][k];
if(k) f[i][0][k] += f[i-1][2][k-1];
if(k == 3) f[i][0][k] += f[i-1][2][k];
f[i][0][k] += 25*f[i-1][0][k] + 24*f[i-1][1][k] + 25*f[i-1][2][k];
f[i][0][k]%=mod,f[i][1][k]%=mod,f[i][2][k]%=mod;
}
cout<<(f[n][0][3]+f[n][1][3]+f[n][2][3]) % mod<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
集训营的气球(balloon)
动态dp or 线段树维护dp?
赛时写的\(O(n^2q)\)的
80pts部分分:线段树维护dp
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("balloon.in"),*OutFile = outfile("balloon.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10,mod = 1e9 + 7;
namespace IO{
char buf[1<<23],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
#define pc putchar_unlocked
template<class T>
inline void read(T &x){
x = 0;
char s = gc();
for(;s < '0' || s > '9';s = gc());
for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);
}
template<class T,class... Args>
inline void read(T &x,Args&... argc){read(x);read(argc...);}
template<class T>
inline void write(T x){
static int sta[20],top = 0;
do{sta[++top] = x%10,x /= 10;}while(x);
do{pc(sta[top--]+'0');}while(top);
}
}using namespace IO;
template#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("tree.in"),*OutFile = outfile("tree.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
mt19937 rnd((ull)(new char));
cout<<299<<'\n';
}
<class T>
inline int Mod(T x){return x>mod?x-mod:x;}
int n,c,q,a[N],b[N],pos[N],fa[N<<1];
struct Segment_Tree{
struct segment_tree{
int ls,rs,dp[21],tot;
#define ls(k) tree[k].ls
#define rs(k) tree[k].rs
#define dp(k) tree[k].dp
#define tot(k) tree[k].tot
}tree[N<<1];
int tot = 0,rt;
inline void pushup(int k){
rep(i,0,c-1,1) dp(k)[i] = 0;
int ls = ls(k),rs = rs(k);
rep(i,0,c-1,1) rep(j,0,c-i-1,1)
dp(k)[i+j] = Mod(dp(k)[i+j]+1ll*dp(ls)[i]*dp(rs)[j]%mod);
tot(k) = 1ll*tot(ls)*tot(rs)%mod;
}
void build(int &k,int l,int r){
k = ++tot;
if(l == r){
dp(k)[0] = b[l],dp(k)[1] = a[l],tot(k) = a[l]+b[l],pos[l] = k;
return;
}
int mid = (l + r) >> 1;
build(ls(k),l,mid);build(rs(k),mid+1,r);
fa[ls(k)] = fa[rs(k)] = k;
pushup(k);
}
inline void update(int p,int x,int y){
int k = pos[p];
dp(k)[0] = y,dp(k)[1] = x,tot(k) = x+y;
k = fa[k];
while(k) pushup(k),k = fa[k];
}
}T;
inline void solve(){
read(n,c);
rep(i,1,n,1) read(a[i]);rep(i,1,n,1) read(b[i]);
T.build(T.rt,1,n);read(q);
while(q--){
int p,x,y;read(p,x,y);
T.update(p,x,y);
int ans = T.tot(1);
rep(i,0,c-1,1) ans = Mod(ans - T.dp(1)[i] + mod);
write(ans),pc('\n');
}
}
signed main(){
// cin.tie(nullptr)->sync_with_stdio(false);
// cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
正解是回退背包。
设\(f_{i,j}\)表示前\(i\)个人已经有\(j\)个人买一血气球。
考虑有dp方程\(f_{i,j} = f_{i-1,j-1}\times a_i+f_{i-1,j}\times b_i\)
那么回退的方程就是\(f_{i,j} = (f_{i,j}-f_{i-1,j-1}\times inv(a_i))*inv(b_i)\)
其中\(inv(x)\)表示\(x\)的逆元。
回退的时候反着做就行,然后\(ans=tot-\sum\limits_{i=0}^{c-1}f_{n,i}\),其中\(tot=\prod\limits_{i=1}^na_i+b_i\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("balloon.in"),*OutFile = outfile("balloon.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10,mod = 1e9 + 7;
namespace IO{
char buf[1<<23],*p1,*p2;
#define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
#define pc putchar_unlocked
template<class T>
inline void read(T &x){
x = 0;
char s = gc();
for(;s < '0' || s > '9';s = gc());
for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);
}
template<class T,class... Args>
inline void read(T &x,Args&... argc){read(x);read(argc...);}
template<class T>
inline void write(T x){
static int sta[20],top = 0;
do{sta[++top] = x%10,x /= 10;}while(x);
do{pc(sta[top--]+'0');}while(top);
}
}using namespace IO;
template<class T>
inline int Mod(T x){return x>mod?x-mod:x;}
template<class T>
inline T power(T a,int b,int mod){
int res = 1;
for(;b;b >>= 1,a = 1ll*a*a%mod) if(b&1) res = 1ll*res*a%mod;
return res;
}
template<class T>
inline int inv(T x){return power(x,mod-2,mod);}
int n,c,q,a[N],b[N],f[21],tot;
inline void solve(){
read(n,c);
rep(i,1,n,1) read(a[i]);rep(i,1,n,1) read(b[i]);
read(q);
tot = f[0] = 1;
rep(i,1,n,1){
drep(j,c-1,1,1) f[j] = Mod(1ll*f[j-1]*a[i]%mod+1ll*f[j]*b[i]%mod);
f[0] = 1ll*f[0]*b[i]%mod;
tot = 1ll*tot*(a[i]+b[i])%mod;
}
while(q--){
int p,x,y;read(p,x,y);
tot = 1ll*tot*inv(a[p]+b[p])%mod*(x+y)%mod;
int invb = inv(b[p]);
f[0] = 1ll*f[0]*invb%mod;
rep(i,1,c-1,1) f[i] = 1ll*(f[i] - 1ll*f[i-1]*a[p]%mod+mod)*invb%mod;
drep(i,c-1,1,1) f[i] = Mod(1ll*f[i-1]*x%mod+1ll*f[i]*y%mod);
f[0] = 1ll*f[0]*y%mod;
a[p] = x,b[p] = y;
int ans = tot;
rep(i,0,c-1,1) ans = Mod(ans-f[i]+mod);
write(ans),pc('\n');
}
}
signed main(){
// cin.tie(nullptr)->sync_with_stdio(false);
// cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
连通子树与树的重心(tree)
看懂题意也不会打的暴力,咋还是回退背包啊。
分两种情况讨论。
-
树有两个重心,分别记为\(a,b\)
将两个重心中间的边断开,生成两颗分别以\(a,b\)为根的树。
记\(f_{x,i}\)表示以\(x\)为根的子树中包含\(x\)且有\(i\)个节点的连通块有多少个,这个树形背包解决。
最后答案为\(\sum\limits_{i=1}^{min(siz_u,siz_v)}f_{u,i}\times f_{v,i}\)
时间复杂度\(O(n^2)\)
-
树只有一个重心,记为\(a\)
和情况1一样,还是求出\(f\)数组,考虑容斥。
发现\(f_{x,i}\)表示包含\(x\)且大小为\(i\)的连通块数量,只需要求出最大子树大小\(>\frac{i}{2}\)的个数即可。
发现如果有一个子树\(Subtree_t\)的大小为\(k\)且\(k>i/2\),那么\(t\)一定只有一个,枚举即可。
但是如果直接减去\(f_{x,i-k}\times f_{t,k}\)会寄,因为\(f_{x,i-k}\)包含了取\(Subtree_t\)的情况。
那么怎么消去这个的贡献呢?
这是一个背包问题,而且物品的次序无关,直接退背包即可。
时间复杂度\(O(n^2)\)
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#define rep(i,s,t,p) for(int i = s;i <= t; i += p)
#define drep(i,s,t,p) for(int i = s;i >= t; i -= p)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("tree.in"),*OutFile = outfile("tree.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 5e3 + 10,mod = 10007;
int n,siz[N],w[N],c[2],cnt,f[N][N],g[N][N];
vector<int> edge[N];
#define eb emplace_back
#define add(u,v) edge[u].eb(v)
void dfs(int x,int fa){
siz[x] = 1,w[x] = 0;
for(int y:edge[x]){
if(y == fa) continue;
dfs(y,x);siz[x] += siz[y];w[x] = max(w[x],siz[y]);
}
w[x] = max(w[x],n-siz[x]);
if(w[x] <= n/2) c[c[0]!=0] = x,++cnt;
}
void DP(int x,int fa){
siz[x] = 1,f[x][0] = f[x][1] = 1;
for(int y:edge[x]){
if(y == fa) continue;
DP(y,x);siz[x] += siz[y];
drep(i,siz[x],1,1)rep(j,1,min(siz[y],i-1),1){
f[x][i] = (f[x][i]+1ll*f[y][j]*f[x][i-j]%mod)%mod;
}
}
}
inline void solve2(){
DP(c[0],c[1]);DP(c[1],c[0]);
int ans = 0;
rep(i,1,min(siz[c[0]],siz[c[1]]),1) ans = (ans + 1ll*f[c[0]][i]*f[c[1]][i]%mod)%mod;
cout<<ans<<'\n';
}
inline void solve1(){
int x = c[0],ans = 0;
DP(x,0);
rep(i,1,n,1) ans = (ans + f[x][i]) % mod;
for(int y:edge[x]){
rep(i,1,n,1){
g[y][i] = f[x][i];
rep(k,1,min(i,siz[y]),1)
g[y][i] = (g[y][i] - g[y][i-k]*f[y][k]%mod+mod)%mod;
}
}
rep(i,1,n,1) for(int y:edge[x]) rep(k,(i+1)/2,min(i,siz[y]),1)
ans = (ans - f[y][k]*g[y][i-k]%mod+mod)%mod;
cout<<ans<<'\n';
}
inline void solve(){
cin>>n;
rep(i,1,n-1,1){int u,v;cin>>u>>v;add(u,v);add(v,u);}
dfs(1,0);
memset(siz,0,sizeof siz);
if(c[1]) solve2();else solve1();
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}