我们刚刚知道那些题的解法-2
CF1292F
考虑对于
设
现在我们就可以考虑一个 DP,设
我们现在有了一个复杂度是
首先,所有大于
其次,设
代码:
int n,a[N],id[N],rd[N],ans,jie[N],invjie[N];
vi e[N],v,S,T;
int ts[N],cnt[M],f[M][N],sum;
bool vis[N];
inline int ksm(int a,int b,int mod){
int res=1;while(b){if(b&1)res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}return res;
}
inline int inv(int a){return ksm(a,mod-2,mod);}
inline void dfs(int k){
vis[k]=1;v.pb(k);
for(int to:e[k])if(!vis[to]){
// printf("k=%d to=%d\n",k,to);
dfs(to);
}
}
inline int DP(){
for(int x:v){
// printf("x=%d ",x);
if(!id[x]) S.pb(a[x]);else T.pb(a[x]);
}
// puts("");
if(T.empty()){
S.clear();return -1;
}
rep(i,0,(int)T.size()-1){
int nows=0;
rep(j,0,(int)S.size()-1){
// printf("T[%d]=%d S[%d]=%d\n",i,T[i],j,S[j]);
if(T[i]%S[j]==0) nows|=(1<<j);
}
cnt[nows]++;ts[i]=nows;
// printf("ts[%d]=%d\n",i,nows);
}
rep(i,0,(int)S.size()-1){
rep(j,0,(1<<((int)S.size()))-1){
if((j>>i)&1) cnt[j]+=cnt[j^(1<<i)];
}
}
// printf("cnt[1]=%d\n",cnt[1]);
rep(i,0,(int)T.size()-1){
f[ts[i]][1]++;
}
rep(i,1,(int)T.size()-1)rep(j,0,(1<<((int)S.size()))-1)if(f[j][i]){
bool op=0;
rep(k,0,(int)T.size()-1){
if((ts[k]&j)==0) continue;
if((ts[k]&j)==ts[k]){
if(op) continue;
else{
// puts("");
f[j][i+1]=(f[j][i+1]+f[j][i]*(cnt[j]-i)%mod)%mod;op=1;
}
}
else f[j|ts[k]][i+1]=(f[j|ts[k]][i+1]+f[j][i])%mod;
}
}
int ans=f[(1<<((int)S.size()))-1][T.size()];
rep(i,0,(1<<((int)S.size()))-1)rep(j,1,T.size()) f[i][j]=0,cnt[i]=0,ts[j-1]=0;
sum+=T.size()-1;ans=ans*invjie[T.size()-1]%mod;
S.clear();T.clear();
return ans;
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);rep(i,1,n) read(a[i]);
jie[0]=1;rep(i,1,n) jie[i]=1ll*jie[i-1]*i%mod;
invjie[n]=inv(jie[n]);dec(i,0,n-1) invjie[i]=invjie[i+1]*(i+1)%mod;
rep(i,1,n)rep(j,1,n){
if(i==j) continue;
if(a[j]%a[i]==0){
// printf("%d %d\n",i,j);
e[i].pb(j);id[j]++;rd[i]++;e[j].pb(i);
}
}
ans=1;sum=0;
// printf("ans=%d\n",ans);
rep(i,1,n)if(!vis[i]){
v.clear();dfs(i);
int nowans=DP();
if(nowans==-1) continue;
ans=ans*nowans%mod;
// printf("nowans=%d\n",nowans);
}
ans=ans*jie[sum]%mod;
printf("%d\n",ans);
return 0;
}
错误总结:
- s,t 忘记清空。
- 组合数贡献算错。
CF1290F
因为是凸包,所以凸包的形状仅和每个向量的个数有关系,设
考虑
因为有进位,所以我们从小到大考虑填什么,在二进制下考虑,因为这样枚举
int n,m,x[N],y[N],f[31][M][M][M][M][2][2];
inline int ge(int a,int b,int c){
if(a!=b) return (a<b)?0:1;return c;
}
inline int dfs(int p,int px,int py,int nx,int ny,int limx,int limy){
if(p==30) return (!px&&!py&&!nx&&!ny&&!limx&&!limy);
int &val=f[p][px][py][nx][ny][limx][limy];if(val!=-1) return val;
val=0;int dm=(m>>p)&1;
rep(i,0,(1<<n)-1){
int npx=px,npy=py,nnx=nx,nny=ny;
rep(j,0,n-1){
if((i>>j)&1){
(x[j]>0)?npx+=x[j]:nnx-=x[j];
(y[j]>0)?npy+=y[j]:nny-=y[j];
}
}
int dnpx=npx&1,dnpy=npy&1,dnnx=nnx&1,dnny=nny&1;
if(dnpx==dnnx&&dnpy==dnny){
val=(val+dfs(p+1,npx>>1,npy>>1,nnx>>1,nny>>1,ge(dnpx,dm,limx),ge(dnpy,dm,limy)))%mod;
}
}
return val;
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(m);rep(i,0,n-1) read(x[i]),read(y[i]);
mset(f,-1);
int ans=dfs(0,0,0,0,0,0,0)-1;
printf("%d\n",(ans+mod)%mod);
return 0;
}
数位 DP,如果不牵扯到进位,那么从大往小 DP 易做限制,否则的话就从小往大做。
CF1286F
考虑所有操作
假设我们能判断一个集合
需要减枝,所以我们用自己更新别人。也就是说,我们枚举一个集合
至于孤立点,我们其实并没有理睬他们,他们的 DP 数组永远是
现在考虑 Check
,我们可以把整棵树黑白染色,染成二分图,而左部点点权和与右部点点权和的差值的绝对值一定是小于等于树的大小减
采用折半搜索,搜索过程中把两边可能的集合差值从小到大排序,然后双指针看是否有一对数满足条件。
值得注意的是,如果集合中所有点的点权和小于等于树的大小减
可以证明,不管左部点和右部点的个数是多少,只要不为
int n,a[N],f[M],lg[M],b[N],bt,sum[M];
inline vi Get(int nl,int nr){
vi v;v.clear();
if(nl==nr){
if(b[nl]>0){v.pb(-b[nl]);v.pb(b[nl]);}
else{v.pb(b[nl]);v.pb(-b[nl]);}return v;
}
v=Get(nl+1,nr);
vi v1,v2;v1.clear();v2.clear();
rep(i,0,(int)v.size()-1) v1.pb(v[i]-b[nl]);
rep(i,0,(int)v.size()-1) v2.pb(v[i]+b[nl]);
vi now;now.clear();
int l=0,r=0;
while(l<(int)v1.size()&&r<(int)v2.size()){
if(v1[l]<v2[r]) now.pb(v1[l]),l++;
else now.pb(v2[r]),r++;
}
// printf("now.size()=%d l=%d r=%d\n",now.size(),l,r);
while(l<(int)v1.size()) now.pb(v1[l]),l++;
while(r<(int)v2.size()) now.pb(v2[r]),r++;
// printf("v1.size()=%d v2.size()=%d\n",v1.size(),v2.size());
// printf("now.size()=%d\n",now.size());
return now;
}
inline bool Check(int S){
if(__builtin_popcount(S)==1){
if(sum[S]==0) return 1;return 0;
}
int siz=__builtin_popcount(S)-1;
if((sum[S]-siz)&1) return 0;bt=0;
rep(i,0,n-1) if((S>>i)&1) b[++bt]=a[i+1];int mid=(1+bt)>>1;
vi L=Get(1,mid),R=Get(mid+1,bt);
// printf("mid=%d\n",mid);
int l=R.size(),r=R.size()-1;
// puts("L:");for(int x:L) printf("%d ",x);puts("");
// puts("R:");for(int x:R) printf("%d ",x);puts("");
int nd=1+(abs(sum[S])<=siz)*2;
// printf("nd=%d\n",nd);
for(int i=0;i<L.size();i++){
while(l&&L[i]+R[l-1]>=-siz) l--;
while(r>=0&&L[i]+R[r]>siz) r--;
// printf("l=%d r=%d\n",l,r);
nd-=min(nd,r-l+1);
// if(l<=r&&r!=R.size()) return 1;
// if(r==-1) return 0;
}
return (nd==0);
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);rep(i,1,n) read(a[i]),lg[1<<i]=i;
rep(i,1,n) sum[1<<(i-1)]+=a[i];
rep(i,0,n-1)rep(j,0,(1<<n)-1) if((j>>i)&1) sum[j]+=sum[j^(1<<i)];
lg[1]=0;
// printf("Check(7)=%d\n",Check(7));
for(int T=0;T<(1<<n);T++){
if(!f[T]&&Check(T)){
// puts("Now");
int t=T^((1<<n)-1);
for(int S=t;;S=(S-1)&t){
// printf("T|S=%d T=%d S=%d\n",T|S,T,S);
cmax(f[T|S],f[S]+1);
if(!S) break;
}
}
// printf("f[%d]=%d\n",T,f[T]);
}
printf("%lld\n",n-f[(1<<n)-1]);
return 0;
}
如果需要卡常,那么用自己更新别人加上减枝可能是一个很好的方法,因为如果转移
CFRound 819E
这个题没做出来真的是比较失败,感觉就差一点。
首先考虑环的种类,一共是有
考虑计算贡献,不妨枚举最后一种环的个数,设为
int t,n,jie[N],invjie[N],I[N];
inline int ksm(int a,int b,int mod){
int res=1;while(b){if(b&1)res=1ll*a*res%mod;a=1ll*a*a%mod;b>>=1;}return res;
}
inline int inv(int a){return ksm(a,mod-2,mod);}
inline int C(int n,int m){
if(n<m) return 0;return jie[n]*invjie[m]%mod*invjie[n-m]%mod;
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(t);
jie[0]=1;rep(i,1,Len) jie[i]=jie[i-1]*i%mod;
invjie[Len]=inv(jie[Len]);dec(i,0,Len-1) invjie[i]=invjie[i+1]*(i+1)%mod;
I[1]=1;I[0]=1;rep(i,2,Len) I[i]=(I[i-1]+(i-1)*I[i-2]%mod)%mod;
while(t--){
read(n);
int ans=0;
rep(i,0,n/4){
int nowans=C(n-2*i,2*i)*jie[2*i]%mod*invjie[i]%mod;
ans=(ans+nowans*I[n-i*4]%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}
P3911
令
HDU5328
有:
考虑第二个限制条件等价于
其中,
CF1279F
考虑设 DP
int n,K,l,a[N],cnt[N][2],f[N],g[N],Ans,maxx,maxxid;
string s;
//最大化 id
inline bool DP(int id,int mid){
// printf("id=%d mid=%d\n",id,mid);
rep(i,1,n) f[i]=0,g[i]=0;
maxx=-INF;
rep(i,0,l-1) f[i]=cnt[i][id];
cmax(maxx,f[0]-cnt[0][id^1]);
maxxid=0;
rep(i,l,n){
f[i]=cnt[i-l][id]+maxx-mid+l;
g[i]=maxxid+1;
// cmax(maxx,f[i-l+1]-cnt[i-l+1][id^1]);
if(maxx<f[i-l+1]-cnt[i-l+1][id]){
maxx=f[i-l+1]-cnt[i-l+1][id];
maxxid=g[i-l+1];
}
}
// rep(i,0,n){
// printf("f[%d]=%d g[%d]=%d\n",i,f[i],i,g[i]);
// }
maxx=-INF;maxxid=-1;
rep(i,0,n){
if(maxx<f[i]+cnt[n][id]-cnt[i][id]){
maxx=f[i]+cnt[n][id]-cnt[i][id];maxxid=g[i];
}
}
// printf("maxx=%d maxxid=%d\n",maxx,maxxid);
return maxxid<=K;
}
inline void Solve(int id){
int L=0,R=n;
while(L<R){
int mid=(L+R)>>1;
if(DP(id,mid)) R=mid;else L=mid+1;
}
DP(id,L);cmax(Ans,maxx+L*K);
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(K);read(l);cin>>s;int lens=s.length()-1;
rep(i,0,lens) if(s[i]>='a'&&s[i]<='z') a[i+1]=0;else a[i+1]=1;
if(K*l>=n){
puts("0");return 0;
}
rep(i,1,n){
cnt[i][0]=cnt[i-1][0];cnt[i][1]=cnt[i-1][1];
cnt[i][a[i]]++;
}
Solve(0);
// printf("Ans=%d\n",Ans);
Solve(1);
// printf("Ans=%d\n",Ans);
printf("%lld\n",n-Ans);
return 0;
}
/*
+ 统计答案出错
+ 应为 L*K 而非 L*maxxid
+ 预处理出错
+ 忘开 long long
*/
CF1276D
考虑删点序列计数等价于删点方案计数,两个方法不同当且仅当存在至少一条边选择的点不同。因此可以考虑 DP。
一个点有
转移举一个例子,例如考虑
int n,f[N][4],pre[N],suf[N];
vc<P> e[N];
inline void dfs(int k,int fa){
for(P to:e[k])if(to.se!=fa){dfs(to.se,k);}
rep(i,0,(int)e[k].size()-1) pre[i]=suf[i]=0;
int id=-1;rep(i,0,(int)e[k].size()-1)if(e[k][i].se==fa){id=i;break;}
int faid=e[k][id].fi;
e[k].erase(e[k].begin()+id);
if(e[k].size()==0){
f[k][0]=1;f[k][2]=1;return;
}
rep(i,0,(int)e[k].size()-1){
if(i==0) pre[i]=(f[e[k][i].se][1]+f[e[k][i].se][2])%mod;
else pre[i]=1ll*pre[i-1]*((f[e[k][i].se][1]+f[e[k][i].se][2])%mod)%mod;
}
suf[e[k].size()]=1;
dec(i,0,(int)e[k].size()-1){
suf[i]=1ll*suf[i+1]*((f[e[k][i].se][0]+f[e[k][i].se][1]+f[e[k][i].se][3])%mod)%mod;
}
rep(i,0,(int)e[k].size()-1){
int nowans=1;
if(i!=0) nowans=nowans*pre[i-1]%mod;
if(i!=(int)e[k].size()-1) nowans=nowans*suf[i+1]%mod;
nowans=nowans*(f[e[k][i].se][0]+f[e[k][i].se][3])%mod;
if(e[k][i].fi<faid){
f[k][1]=(f[k][1]+nowans)%mod;
}
else{
f[k][3]=(f[k][3]+nowans)%mod;
}
}
f[k][0]=pre[(int)e[k].size()-1];
f[k][2]=1;
rep(i,0,(int)e[k].size()-1){
if(e[k][i].fi<faid){
f[k][2]=f[k][2]*(f[e[k][i].se][1]+f[e[k][i].se][2])%mod;
}
else{
f[k][2]=f[k][2]*(f[e[k][i].se][0]+f[e[k][i].se][1]+f[e[k][i].se][3])%mod;
}
}
// rep(i,0,3) printf("f[%d][%d]=%d\n",k,i,f[k][i]);
return;
}
signed main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);
rep(i,1,n-1){
int u,v;read(u);read(v);e[u].pb(mp(i,v));e[v].pb(mp(i,u));
}
e[1].pb(mp(0,0));
rep(i,1,n) sort(e[i].begin(),e[i].end());
dfs(1,0);
int ans=0;
ans=(f[1][0]+f[1][1]+f[1][3])%mod;
printf("%lld\n",ans);
return 0;
}
CF1225G
考虑如果能有一组答案,那么一定满足能有一组
于是我们可以有一组 bitset
进行加速。
int n,k,a[N],sum,b[N];
bitset<2001> f[N];
priority_queue<P > q;
inline void ge(int s,int x){
if(!s) return;
if(x*k<=2000&&f[s][x*k]==1){
rep(i,0,n-1) if((s>>i)&1) b[i+1]++;
ge(s,x*k);return;
}
rep(i,1,n) if(((s>>(i-1))&1)&&x>=a[i]){
if(f[s^(1<<(i-1))][x-a[i]]){ge(s^(1<<(i-1)),x-a[i]);return;}
}
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(n);read(k);rep(i,1,n) read(a[i]);f[0][0]=1;
rep(i,1,n) sum+=a[i];
for(int i=0;i<(1<<n);i++){
rep(j,1,n) if((i>>(j-1))&1){
// puts("Here");
// cout<<f[i^(1<<(j-1))]<<endl;
// printf("a[j]=%d\n",a[j]);
f[i]|=(f[i^(1<<(j-1))]<<a[j]);
}
dec(j,1,sum/k){
// f[i][j]=f[i][j*k];
if(f[i][j*k]){
// printf("j=%d\n",j);
f[i][j]=1;
}
}
// printf("i=%d:\n",i);
// cout<<f[i]<<endl;
}
if(!f[(1<<n)-1][1]){puts("NO");return 0;}
else{
puts("YES");
ge((1<<n)-1,1);
// rep(i,1,n) printf("b[%d]=%d\n",i,b[i]);
rep(i,1,n) q.push({b[i],a[i]});
while(q.size()>1){
P n1=q.top();q.pop();
P n2=q.top();q.pop();
printf("%d %d\n",n1.se,n2.se);
n1.se+=n2.se;
while(n1.se%k==0){
n1.se/=k;n1.fi--;
}
q.push(n1);
}
}
return 0;
}
犯的错误:
- DP 数组第二种转移赋值出现错误。
- 递归没设置结束条件
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律