签到题 把选三个数相加拆为选择一个数,然后看前面有没有能用两个数组合出答案。 $ O(n^2) $ 。 也算签到题吧,但我被评测机创飞了。 直接暴力枚举写了个狗屎DP,算了下时间来到了 4e8。。。但加了一点剪枝后在本地跑了0.64s,然后自信交卷了。然后就T了! 说下改进后的DP 一维状态记已经有了多少个 $ SOS $ ,另一维表示目前最后三个字符的状态,分别有 第一个有用的 $ S $ ,第一个有用的 $ O $ 、第二个有用的 $ S $ 和其他状态。直接转移即可。 这个确实快,1e8本地只跑了0.94s。 退背包的板子吧。 第一次接触退背包的题,但是考场上竟然YY出来了。 首先背包肯定会跑,但是每次修改都要重新跑一边背包的话复杂度就太高了。 其实但是打完这个就去看T4了,没想着能A了,但后面那个T4属实不太会,于是又回来看T3。 看看我们能不能把修改的复杂度降下来。先想想为什么线段树比暴力快呢?或者说为什么暴力很慢呢?因为每次重新跑一遍DP会丢失很多之前已经统计好的信息,然后又白白的去统计一堆已经统计过的信息,但其实有用的信息只有修改的地方改变了,那么线段树之所以快,就是因为他重新统计的信息少,浪费的信息少。那么我们能不能不浪费任何信息呢? 其实是可以的,背包与物品的顺序是无关的,那么我们可以假设每次修改的位置都是最后一个物品,那么第 n-1 个及以前的物品是不变的,再跑一次DP时跑到这的DP数组也是一点不变的,但我们又不能每次记倒数第二个DP数组,因为他并不是真的只改最后一个,只是假设而已,那么我们就得设法通过最终状态的DP数组求出 去除修改物品后的DP数组。 这个就叫退背包,我们通过正向的转移柿子去逆向解出原来的DP数组。 那这个题举例子吧: 设 $ f_i ,i∈[0,C] $ 表示有 $ i $ 个人选择的是 一血气球, $ f_i , [i=C+1] $ 表示至少有 $ C+1 $ 个人选择一血气球。pos 表示当前修改的位置。 我们可以写出转移柿子 那么我们把pos当做最后一个位置,于是就可以把柿子倒回去。设 $ g_i $ 表示不考虑 pos 这个位置时的 $ f_i $ 。 那么直接把上面的等式右边的 $ f_i $ 修改为 $ g_i $ 即可 怎么解呢,高斯消元? 其实不用,我们要知道 $ g_{C+1} $ 就得知道 $ g_C $ ,要知道 $ g_C $ ,就得知道 $ g_{C-1} $,以此类推,最后压力给到了 $ g_0 $ ,那 $ g_0 $ 是什么呢,就是除去 $ pos $ 这个位置上的人,所有人都选 b 的方案数。这个直接那所有的除个 $ b_{pos} $ 就行了。 求出 $ g_i $ 后,就可以求出新的 $ f_i $ 了,那么我们对于每次修改,只需要 $ O(C) $,的时间复杂度就可以处理了! 至此,就是场上想的退背包的过程,要学习退背包可以去专门学学,有板子题:P4141 消失之物。 先膜拜一下Qyun,全场唯一一个拿分而且拿了50分的。 其实当时看到这个题的时候,有点抵触,因为这种题其实我没什么头绪,整了半个小时也没整出来啥,就放弃了。 这道题主要就是用重心的性质。 不太想写了,挂个题解吧。T1、好数(number)
码(
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define mk make_pair
#define ps push_back
#define fi first
#define se second
const int N=1e6+10,inf=0x3f3f3f3f,B=2e5+10;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
char c=getchar_unlocked();ll x=0,f=1;
while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked();
return x*f;
}
int n,a[N];
bool f[N];
signed main(){
// #ifndef ONLINE_JUDGE
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
// #endif
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
n=read();
for(int i=1;i<=n;++i)
a[i]=read();
int ans=0;
for(int i=1;i<=n;++i){
for(int j=1;j<i;++j){
if(f[a[i]-a[j]+B]){++ans;break;}
}
for(int j=1;j<=i;++j)
f[a[i]+a[j]+B]=1;
}
cout<<ans;
}
T2、SOS字符串(sos)
所以评测机一秒到底能跑多少啊。码(
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define mk make_pair
#define ps push_back
#define fi first
#define se second
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
char c=getchar_unlocked();ll x=0,f=1;
while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked();
return x*f;
}
#define mo(x) (x>=mod?x-=mod:0)
int n;
ll f[2][4][4];
signed main(){
freopen("sos.in","r",stdin);
freopen("sos.out","w",stdout);
// int ti=clock();
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
n=read();
f[0][3][0]=1;
int now=0,to=1;
for(int i=0;i<n;++i){
for(int k=0;k<=2;++k){
(f[to][3][k]+=((f[now][0][k]+f[now][1][k]+f[now][2][k]+f[now][3][k])*25ll-f[now][0][k]))%=mod;
(f[to][1][k]+=f[now][0][k]);f[to][1][k]>=mod?f[to][1][k]-=mod:0;
(f[to][0][k]+=(f[now][3][k]+f[now][2][k]+f[now][0][k]))%=mod;
(f[to][2][k+1]+=f[now][1][k]);f[to][2][k+1]>=mod?f[to][2][k+1]-=mod:0;
f[now][0][k]=f[now][1][k]=f[now][2][k]=f[now][3][k]=0;
}
(f[to][2][3]+=f[now][2][3]*26ll)%=mod;f[now][2][3]=0;
swap(now,to);
}
cout<<f[now][2][3]<<'\n';
// cout<<(clock()-ti)/1e6<<endl;
}
T3、集训营的气球(balloon)
但是你可以观察到每次只会修改一个地方,然后全局询问,那么你就可以用线段树去维护这个背包,直接单点修改即可,非常的好写。时间复杂度是 O $ (q c^2 log(n)) $,但是有点小卡常,反正我不用光速膜是过不去80分的点的。线段树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define mk make_pair
#define ps push_back
#define fi first
#define se second
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
inline ll read(){
char c=getchar_unlocked();ll x=0,f=1;
while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked();
return x*f;
}
int f[N<<1][21],a[N],b[N],n,C;
#define mo(x) (x>=mod?x-=mod:0)
inline void up(int a[],int b[],int c[]){
for(int i=0;i<=C;++i)
a[i]=0;
for(int i=0;i<=C;++i){
for(int j=0;j<=C;++j)
(a[i+j>C?C:i+j]+=(ll)b[i]*c[j]%mod)%=mod;
}
}
inline void add(int k,int l,int r,int pos){
if(l==r)return (void)(f[k][0]=b[l],f[k][1]=a[l]);
int mid=l+r>>1;
pos<=mid?add(k<<1,l,mid,pos):add(k<<1|1,mid+1,r,pos);
for(int i=0;i<=C;++i)
f[k][i]=0;
for(int i=0;i<=C;++i){
for(int j=0;j<=C;++j)
f[k][i+j>C?C:i+j]+=(ll)f[k<<1][i]*f[k<<1|1][j]%mod,mo(f[k][i+j>C?C:i+j]);
}
}
inline void jian(int k,int l,int r){
if(l==r)return (void)(f[k][0]=b[l],f[k][1]=a[l]);
int mid=l+r>>1;
jian(k<<1,l,mid),jian(k<<1|1,mid+1,r);
for(int i=0;i<=C;++i){
for(int j=0;j<=C;++j)
f[k][i+j>C?C:i+j]+=(ll)f[k<<1][i]*f[k<<1|1][j]%mod,mo(f[k][i+j>C?C:i+j]);
}
}
signed main(){
// #ifndef ONLINE_JUDGE
freopen("balloon.in","r",stdin);
freopen("balloon.out","w",stdout);
// #endif
// double ti=clock();
// cout<<sizeof(f)/1024/1024<<endl;
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
n=read();C=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
b[i]=read();
jian(1,1,n);
int m=read();
for(int j=1,pos;j<=m;++j){
pos=read();a[pos]=read(),b[pos]=read();
add(1,1,n,pos);
cout<<f[1][C]<<'\n';
}
// cout<<(double)(clock()-ti)/1e6<<'\n';
}
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define mk make_pair
#define ps push_back
#define fi first
#define se second
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=1e9+7;
#define int ll
inline ll read(){
char c=getchar_unlocked();ll x=0,f=1;
while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked();
return x*f;
}
ll f[2][22],a[N],b[N],ff[22],C,n;
inline ll qpow(ll x,ll y){
ll ans=1;x%=mod;
while(y){
if(y&1)ans=ans*x%mod;
x=x*x%mod;y>>=1;
}
return ans;
}
signed main(){
// #ifndef ONLINE_JUDGE
freopen("balloon.in","r",stdin);
freopen("balloon.out","w",stdout);
// #endif
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
n=read(),C=read();
ll sumb=1;
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
b[i]=read(),sumb=sumb*b[i]%mod;
int now=1,fr=0;
f[0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=C+1;++j){
// f[now][j]=0;
f[now][j]=f[fr][j]*b[i]%mod;
if(j)f[now][j]+=f[fr][j-1]*a[i]%mod;
if(j==C+1)f[now][j]+=f[fr][j]*a[i]%mod;
f[now][j]%=mod;
}
swap(now,fr);
}
for(int i=0;i<=C+1;++i)
f[0][i]=f[fr][i];
int m=read();
for(int i=1,pos;i<=m;++i){
pos=read();
ll nyb=qpow(b[pos],mod-2);
ff[0]=sumb*nyb%mod;
for(int j=1;j<=C;++j){
ff[j]=((f[0][j]-ff[j-1]*a[pos]%mod)*nyb%mod+mod)%mod;
}
ff[C+1]=((f[0][C+1]-ff[C]*a[pos]%mod)*qpow(a[pos]+b[pos],mod-2)%mod+mod)%mod;
a[pos]=read(),b[pos]=read();sumb=sumb*nyb%mod*b[pos]%mod;
for(int j=0;j<=C+1;++j){
f[0][j]=ff[j]*b[pos]%mod;
if(j)f[0][j]+=ff[j-1]*a[pos]%mod;
if(j==C+1)f[0][j]+=ff[j]*a[pos]%mod;
f[0][j]%=mod;
}
cout<<(f[0][C]+f[0][C+1])%mod<<'\n';
}
}
T4、连通子树与树的重心(tree)
咕了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define mk make_pair
#define ps push_back
#define fi first
#define se second
const int N=1e6+10,inf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f,mod=10007;
inline ll read(){
char c=getchar_unlocked();ll x=0,f=1;
while(!isdigit(c))f=c=='-'?-1:1,c=getchar_unlocked();
while(isdigit(c))x=(x<<1)+(x<<3)+(c^48),c=getchar_unlocked();
return x*f;
}
int n,cnt,hd[N],rt,sz[N],man[N],rt2;
int f[5001][5001],g[5001];
struct jj{
int to,next;
}bi[N<<1];
inline void add(int x,int y){bi[++cnt]={y,hd[x]},hd[x]=cnt,bi[++cnt]={x,hd[y]},hd[y]=cnt;}
inline void findrt(int x,int f){
sz[x]=1,man[x]=0;
for(int i=hd[x];i;i=bi[i].next){int j=bi[i].to;if(j!=f)findrt(j,x),sz[x]+=sz[j],man[x]=max(man[x],sz[j]);}
man[x]=max(man[x],n-sz[x]);
if(man[x]<=(n>>1))rt=x;
}
inline void dfs(int x,int ff){
f[x][1]=1;sz[x]=1;
for(int i=hd[x];i;i=bi[i].next){
int j=bi[i].to;
if(j!=ff){
dfs(j,x);
for(int p=sz[x];p;--p){
for(int k=1;k<=sz[j];++k)
(f[x][p+k]+=f[x][p]*f[j][k]%mod)%=mod;
}
sz[x]+=sz[j];
}
}
}
signed main(){
// #ifndef ONLINE_JUDGE
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
// #endif
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
n=read();
for(int i=1;i<n;++i)
add(read(),read());
rt=1;
findrt(1,0);
for(int i=hd[rt];i;i=bi[i].next){
int j=bi[i].to;
if(man[j]==man[rt])rt2=j;
}
if(rt2){
dfs(rt,rt2),dfs(rt2,rt);
int ans=0;
for(int i=1;i<=n;++i)
(ans+=f[rt][i]*f[rt2][i]%mod)%=mod;
cout<<ans<<'\n';
return 0;
}
else{
dfs(rt,0);
//tuibao
int ans=0;
for(int i=1;i<=n;++i)
ans=(ans+f[rt][i])%mod;
for(int i=hd[rt];i;i=bi[i].next){
int j=bi[i].to;
for(int k=1;k<=n;++k){
g[k]=f[rt][k];
for(int p=1;p<=min(k,sz[j]);++p){
g[k]=(g[k]-g[k-p]*f[j][p]%mod+mod)%mod;
}
for(int p=(k+1)>>1;p<=sz[j]&&p<=k;++p)
ans=(ans-g[k-p]*f[j][p]%mod+mod)%mod;
}
}
cout<<ans<<'\n';
}
}
hard to say
今天晚上开了动员大会,怎么说呢。算是点醒我吗?
今天看见暑假集训模拟赛的结果,退了不少,一开始还是在前面的,但是后面几场几乎是垫底了。
因为点什么呢,大概是当时有点拒绝思考吧,可能就确实是怕输吧。
但怕有什么用呢,就好好打模拟赛吧,也不要沾沾自喜,也不要患得患失,就学吧。
那我后悔学信奥吗,其实总来没后悔过,并不是因为今天huge说的话,而只是因为,就确实不后悔,其实平常总开玩笑,“还好我是信奥的”,但一点不后悔。放下架子,往前走吧。