校oj很多题目还是很好的 考虑给他们蟹蟹题解Y
打扫卫生 题目大意:n个房间 m个同学 每个房间至少两个同学 求方案数
显然是个计数类的题目 技术类题目 真的 需要有一定数学感觉8... 头有点大奥
然后我们考虑这道题目显然是一个组合数的问题 然后需要用到隔板法 所以在这里 我浅析一下隔板法的应用 有多学习了一个好东西
具体隔板法 对于n个有顺序的球 我们考虑在中间放入m个板 然后规定我们不能在两个球之间放两个及以上个隔板 然后两段不能放隔板 问你方案数
我们不妨考虑 在这n-1个空位置 放入m个球 那么方案数 显然是$\binom{n-1}{m}$
据说今年高联数学二试也考了个隔板法?? 还是比较好写的那个题目 到底是形如什么样的式子 可以 使用隔板法求解问题呢
我们不妨举几个例子
求解$\sum_{i=1}^{m} x_i=n$ 有多少个正整数解
对于这种情况 我们可以考虑是在n个球中插入了 m-1个板 类比刚才的方法 容易得到方案数数为$\binom{n-1}{m-1}$
然后考虑 对于求解$\sum_{i=1}^{m} x_i=n$ 有多少个自然数解 也就是说 你可以存在$x_i=0$ 所以我们为了避免这种情况出现
我们可以对于每一个$x_i+1$ 所以 我们的式子变成了 $\sum_{i=1}^{m} (x_i+1)=n+m$ 所以此时换元 每一个$t_i$ 就是正整数了
所以我们可以类比刚才的方法做了 然后方案数就是$\binom{n+m-1}{m-1}=\binom{n+m-1}{n}$
然后这道题目保证每个房间至少两个人 所以我们不妨 先将这 n*2个同学T出去 因为一定要留下来这2*n个同学 至于剩下的m-2*n个同学
我们考虑 转化成刚才我们讨论过的问题 我们除去这些2*n个人之后 我们 每个房间的人数$x_i$ 这时候就是求解 $\sum_{i=1}^{n} x_i=m-2*n$
对于这种情况 我们$x_i$ 存在 等于0的情况 我们进一步 转化成 整数情况 $\sum_{i=1}^{n} (x_i+1)=m-2*n+n$ 然后类比刚才的例子1 我们就求出了答案
果然是讨论问题 所以这个题目 还有写 高精*单精 就没了
#include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=1010; int n,m,top,b[N],p0[N],p1[N],p2[N]; inline void insert(int x,int *a) { int now=x; for(int i=2;i*i<=x;i++) { while(now%i==0) { now=now/i; ++a[i]; } } if(now>1) ++a[now]; } inline void mul(int x) { int res=0; for(int i=1;i<=top;i++) { b[i]=b[i]*x; b[i]+=res; res=b[i]/10; b[i]=b[i]%10; } while(res) { b[++top]=res; res=b[top]/10; b[top]=b[top]%10; } } int main() { read(n); read(m); if(m<2*n) {puts("0");return 0;} int s=m-2*n+n-1,c=m-2*n; for(int i=2;i<=s;i++) insert(i,p0); for(int i=2;i<=c;i++) insert(i,p1); for(int i=2;i<=s-c;i++) insert(i,p2); b[++top]=1; for(int i=2;i<=s;i++) { p0[i]=p0[i]-p1[i]-p2[i]; while(p0[i]) { --p0[i]; mul(i); } } for(int i=top;i>=1;i--) printf("%d",b[i]); return 0; }
pigs 一道很有意思的网络流呢 这个建图 我着实 思考了一会 不过在Chdy的指导下 还是搞出来了 是个最大流
具体怎么建图呢 因为非常容易想到的是 我们肯定是源点向每个猪小屋连边 容量是每个猪小屋原来拥有的猪猪数量 然后 每个人向汇点连边
然后容量是每个人 原来想要购买的 猪猪的数量 那么考虑 猪小屋 和 顾客之间怎么连边 我们考虑一个事情是 每个猪小屋第一个打开他的 顾客是一定的
当前这个顾客购买完之后 我们考虑 如何将当前这个小屋的猪进行一个转移 就是指向一个接下来能打开他的顾客 所以我们对于猪小屋和顾客
我们将每一个猪小屋 和第一个能打开他的 顾客 连边 容量是 inf 然后将当前第一个顾客 和 能够再次打开这个猪小屋的顾客连边 然后我们就实现了 猪的转移
跑一边最大流即可 我我我我最大流还是写错了 也就是=写成==不报错呗hh
#include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int inf=0x7fffffff; const int N=1000010; struct gg { int y,v,next; }a[N<<1]; int n,m,x,y,z,tot,s,t,H,T,lin[N],q[N],d[N],st[N]; inline void add(int x,int y,int v) { a[++tot].y=y; a[tot].next=lin[x]; lin[x]=tot; a[tot].v=v; a[++tot].y=x; a[tot].next=lin[y]; lin[y]=tot; a[tot].v=0; } inline bool bfs() { memset(d,0,sizeof(d)); H=T=0;q[++T]=s;d[s]=1; while(H++<T) { int x=q[H]; for(int i=lin[x];i;i=a[i].next) { if(a[i].v&&!d[a[i].y]) { q[++T]=a[i].y; d[a[i].y]=d[x]+1; if(a[i].y==t) return 1; } } } return 0; } inline int dinic(int x,int flow) { if(x==t) return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=a[i].next) { if(a[i].v&&d[a[i].y]==d[x]+1) { k=dinic(a[i].y,min(rest,a[i].v)); if(!k) d[a[i].y]=0; a[i].v-=k;a[i^1].v+=k; rest-=k; } } return flow-rest; } int main() { // freopen("1.in","r",stdin); tot=1; read(m); read(n); s=1,t=2+n+m; for(int i=1;i<=m;i++) { read(x); add(1,i+1,x); } for(int i=1;i<=n;i++) { read(x); for(int j=1;j<=x;j++) { read(y); if(!st[y]) add(1+y,m+i+1,inf),st[y]=i+m+1; else add(st[y],m+i+1,inf); } read(z); add(m+i+1,t,z); } int flow=0,max_flow=0; while(bfs()) { while(flow=dinic(s,inf)) max_flow+=flow; } cout<<max_flow<<endl; return 0; }
好像在模拟赛总结的时候 写过这个题目 所以直接树状数组就行了 或者排序后二分 树状数组开了map 所以比较慢
#include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } typedef long long ll; const int N=1000010; ll n,X,a[N]; map<ll,ll>c; inline ll lowbit(ll x) { return x&(-x); } inline void add(ll x) { for(ll i=x;i<=10001000;i+=lowbit(i)) c[i]+=1; } inline ll query(ll x) { ll res=0; for(ll i=x;i>0;i-=lowbit(i)) res+=c[i]; return res; } int main() { //freopen("1..cpp","r",stdin); read(n); read(X); for(int i=1;i<=n;i++) { read(a[i]); add(a[i]); } ll sum=0,t=n*(n-1); for(int i=1;i<=n;i++) { sum+=query(X-a[i]); if(a[i]<=X-a[i]) --sum; } double ans=(double)sum/t; printf("%.2lf",ans); return 0; }
[6.24]子序列累加和 不是连续子序列啊qwq
一眼望去 我想枚举区间 但是这种区间最值得东西 我们不妨思考一种数据结构 这个题目是要我们求最大值最小值的差值
因为我们都知道答案是$\sum_{i=1}^{n} MAX[i]*a[i]-MIN[i]*a[i]$ 其中MAX[i]表示 以i作为区间最大值 的区间的个数 MIN[i]表示以i作为区间最小值的区间个数
考虑$MAX[i]$这些数组怎么求出来 我们可以类比楼兰图腾那道题目 求出$left[i]表示当前这个元素向左边是多少个区间的最值 right[i]$同理 因为自己也可以产生贡献
所以答案是$left[i]*right[i]+1$ 所以我们考虑用单调栈来维护这个东西 具体怎么做呢
以一个例子为例 求出一个元素左边作为多少个区间的最小值 那么
我们显然是需要维护一个 单调递增的栈 然后我们考虑 当前这个元素比栈顶大 那么就加入
否则 不断弹出栈 那么当前的贡献就是栈顶的贡献+1 然后 这里需要考虑的是 你的while循环里 只能有两个等号 也就是同一个方向的是一个等号
不能全是 也不能全不是 这一点要处理好 为什么呢 我们是为了 避免 漏掉 以及 重复的情况 思考一下
//现在有N个数的数列。现在你定义一个子序列是数列的连续一部分 //子序列的值是这个子序列中最大值和最小值之差 //求所有子序列的值得累加和 #include<bits/stdc++.h> using namespace std; typedef long long LL; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=((x<<1)+(x<<3)+(ch^48));ch=getchar();} x*=f; } const int N=300010; LL n; LL a[N],s1[N],s2[N],s3[N],s4[N],sum1[N],sum2[N],sum3[N],sum4[N]; LL top1=0,ans=0,top2=0,top3=0,top4=0; int main() { //freopen("1.in","r",stdin); read(n); for(int i=1;i<=n;i++) { read(a[i]); } s1[++top1]=1; s2[++top2]=n; s3[++top3]=1; s4[++top4]=n; for(LL i=2;i<=n;i++) { for(;a[i]<a[s1[top1]]&&top1;--top1) sum1[i]+=sum1[s1[top1]]+1; s1[++top1]=i; }//正序最小 for(LL i=n-1;i;i--) { for(;a[i]<=a[s2[top2]]&&top2;--top2) sum2[i]+=sum2[s2[top2]]+1; s2[++top2]=i; }//倒序最小 for(LL i=2;i<=n;++i) { for(;a[i]>a[s3[top3]]&&top3;--top3) sum3[i]+=sum3[s3[top3]]+1; s3[++top3]=i; }//正序最大 for(LL i=n-1;i;--i) { for(;a[i]>=a[s4[top4]]&&top4;--top4) sum4[i]+=sum4[s4[top4]]+1; s4[++top4]=i; }//倒序最大 for(LL i=1;i<=n;++i) ans+=(((sum3[i]+1)*(sum4[i]+1))-((sum1[i]+1)*(sum2[i]+1)))*a[i]; cout<<ans<<endl; return 0; }
本来看到 没有思路 但是我们不妨思考一下 每次总是一个数字 除以一个质因数 另一个是 乘上质因数 所以 我们发现 所有数字乘起来
对应的质因数分解 后 质因子对应的指数是不变 所以我们考虑 对于每一个 $a_i$ 我们进行质因数分解 记录一下 每一个数字 对应的质因数 出现的次数
然后 记录一下 这个质因数 一共出现的次数
接下来 我们要保证次数最少 肯定是考虑 将最小的质因数 的指数 均分 给每一个数字了 然后 我们找到这样的数字 可以保证这样的数字一定是存在的 否则最大公约数就是1
但是需要解决的问题还有一个就是空间问题 我第一遍的做法 是 直接MLE了 所以我考虑 改了一下 当前状态 然后开了一个map 解决了这个事情
校oj很多题目还是很好的 考虑给他们蟹蟹题解 打扫卫生 题目大意:n个房间 m个同学 每个房间至少两个同学 求方案数 显然是个计数类的题目 技术类题目 真的 需要有一定数学感觉8... 头有点大奥 然后我们考虑这道题目显然是一个组合数的问题 然后需要用到隔板法 所以在这里 我浅析一下隔板法的应用 有多学习了一个好东西 具体隔板法 对于n个有顺序的球 我们考虑在中间放入m个板 然后规定我们不能在两个球之间放两个及以上个隔板 然后两段不能放隔板 问你方案数 我们不妨考虑 在这n-1个空位置 放入m个球 那么方案数 显然是$\binom{n-1}{m}$ 据说今年高联数学二试也考了个隔板法?? 还是比较好写的那个题目 到底是形如什么样的式子 可以 使用隔板法求解问题呢 我们不妨举几个例子 求解$\sum_{i=1}^{m} x_i=n$ 有多少个正整数解 对于这种情况 我们可以考虑是在n个球中插入了 m-1个板 类比刚才的方法 容易得到方案数数为$\binom{n-1}{m-1}$ 然后考虑 对于求解$\sum_{i=1}^{m} x_i=n$ 有多少个自然数解 也就是说 你可以存在$x_i=0$ 所以我们为了避免这种情况出现 我们可以对于每一个$x_i+1$ 所以 我们的式子变成了 $\sum_{i=1}^{m} (x_i+1)=n+m$ 所以此时换元 每一个$t_i$ 就是正整数了 所以我们可以类比刚才的方法做了 然后方案数就是$\binom{n+m-1}{m-1}=\binom{n+m-1}{n}$ 然后这道题目保证每个房间至少两个人 所以我们不妨 先将这 n*2个同学T出去 因为一定要留下来这2*n个同学 至于剩下的m-2*n个同学 我们考虑 转化成刚才我们讨论过的问题 我们除去这些2*n个人之后 我们 每个房间的人数$x_i$ 这时候就是求解 $\sum_{i=1}^{n} x_i=m-2*n$ 对于这种情况 我们$x_i$ 存在 等于0的情况 我们进一步 转化成 整数情况 $\sum_{i=1}^{n} (x_i+1)=m-2*n+n$ 然后类比刚才的例子1 我们就求出了答案 果然是讨论问题 所以这个题目 还有写 高精*单精 就没了 View Code pigs 一道很有意思的网络流呢 这个建图 我着实 思考了一会 不过在Chdy的指导下 还是搞出来了 是个最大流 具体怎么建图呢 因为非常容易想到的是 我们肯定是源点向每个猪小屋连边 容量是每个猪小屋原来拥有的猪猪数量 然后 每个人向汇点连边 然后容量是每个人 原来想要购买的 猪猪的数量 那么考虑 猪小屋 和 顾客之间怎么连边 我们考虑一个事情是 每个猪小屋第一个打开他的 顾客是一定的 当前这个顾客购买完之后 我们考虑 如何将当前这个小屋的猪进行一个转移 就是指向一个接下来能打开他的顾客 所以我们对于猪小屋和顾客 我们将每一个猪小屋 和第一个能打开他的 顾客 连边 容量是 inf 然后将当前第一个顾客 和 能够再次打开这个猪小屋的顾客连边 然后我们就实现了 猪的转移 跑一边最大流即可 我我我我最大流还是写错了 也就是=写成==不报错呗hh View Code [8.28]概率游戏 好像在模拟赛总结的时候 写过这个题目 所以直接树状数组就行了 或者排序后二分 树状数组开了map 所以比较慢 View Code [6.24]子序列累加和 不是连续子序列啊qwq 一眼望去 我想枚举区间 但是这种区间最值得东西 我们不妨思考一种数据结构 这个题目是要我们求最大值最小值的差值 因为我们都知道答案是$\sum_{i=1}^{n} MAX[i]*a[i]-MIN[i]*a[i]$ 其中MAX[i]表示 以i作为区间最大值 的区间的个数 MIN[i]表示以i作为区间最小值的区间个数 考虑$MAX[i]$这些数组怎么求出来 我们可以类比楼兰图腾那道题目 求出$left[i]表示当前这个元素向左边是多少个区间的最值 right[i]$同理 因为自己也可以产生贡献 所以答案是$left[i]*right[i]+1$ 所以我们考虑用单调栈来维护这个东西 具体怎么做呢 以一个例子为例 求出一个元素左边作为多少个区间的最小值 那么 我们显然是需要维护一个 单调递增的栈 然后我们考虑 当前这个元素比栈顶大 那么就加入 否则 不断弹出栈 那么当前的贡献就是栈顶的贡献+1 然后 这里需要考虑的是 你的while循环里 只能有两个等号 也就是同一个方向的是一个等号 不能全是 也不能全不是 这一点要处理好 为什么呢 我们是为了 避免 漏掉 以及 重复的情况 思考一下 View Code [10.21]调整公约数 本来看到 没有思路 但是我们不妨思考一下 每次总是一个数字 除以一个质因数 另一个是 乘上质因数 所以 我们发现 所有数字乘起来 对应的质因数分解 后 质因子对应的指数是不变 所以我们考虑 对于每一个 $a_i$ 我们进行质因数分解 记录一下 每一个数字 对应的质因数 出现的次数 然后 记录一下 这个质因数 一共出现的次数 接下来 我们要保证次数最少 肯定是考虑 将最小的质因数 的指数 均分 给每一个数字了 然后 我们找到这样的数字 可以保证这样的数字一定是存在的 否则最大公约数就是1 但是需要解决的问题还有一个就是空间问题 我第一遍的做法 是 直接MLE了 所以我考虑 改了一下 当前状态 然后开了一个map 解决了这个事情 #include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } typedef long long ll; int vis[1001000]; int x,n; map<int,map<int,int>>a; const int mod=1000000007; inline ll mul(int a,int b) { ll res=0; while(b) { if(b&1) res=(res+a)%mod; a=(a+a)%mod; b>>=1; } return res; } inline ll power(int a,int b) { ll res=1%mod; while(b) { if(b&1) res=(res*a)%mod; a=mul(a,a); b>>=1; } return res; } int main() { read(n); int ans=1,sum=0,jud; for(int i=1;i<=n;i++) { read(x); for(int j=2;j<=sqrt(x*1.0);j++) { while(x&&x%j==0) { x/=j; ++vis[j]; ++a[i][j];//记录第i个数字 对应的质因数 j 出现的次数 } } ++vis[x]; a[i][x]++; } for(int i=2;i<=1000000;i++) { jud=vis[i]/n; if(jud) { ans=ans*(i,jud); for(int j=1;j<=n;j++) { if(a[j][i]<jud) sum+=jud-a[j][i]; } } } printf("%d %d",ans,sum); return 0; }
这显然是一个 并差集的题目 但是 需要并差集的断开 操作 所以我们不妨 将操作离线 然后 将断开 改成插入 然后 正难则反 好像这个道理只有我刚知道
这里%一下chdy 和 一刀一个小朋友 两个选手 最后 本校oj 需要手动开栈qwq
#include<bits/stdc++.h> using namespace std; char buf[1<<15],*fs,*ft; inline char getc(){ return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:* fs++; } inline int read() { int This=0,F=1; char ch=getc(); while(ch<'0'||ch>'9') {if(ch=='-') F=-1;ch=getc();} while(ch>='0'&&ch<='9') {This=(This<<1)+(This<<3)+ch-'0';ch=getc();} return This*F; } void put(int x) { if(x==0) { putchar('0'); putchar('\n'); return; } if(x<0) { putchar('-'); x=-x; } int num=0;char ch[16]; while(x) ch[++num]=x%10+'0',x/=10; while(num) putchar(ch[num--]); putchar('\n'); } const int N=300100; struct gg { int flag,x; }a[N]; int n,q,ans[N],Next[N],father[N],vis[N]; inline int find(int x) { if(x==-1) return -1; return father[x]==x?x:father[x]=find(father[x]); } int main() { // freopen("1.in.cpp","r",stdin); int __size__ = 20 << 20; // 20MB char *__p__ = (char*)malloc(__size__) + __size__; __asm__("movl %0, %%esp\n" :: "r"(__p__)); n=read(); for(register int i=1;i<=n;i++) Next[i]=read(),father[i]=i; q=read(); for(register int i=1;i<=q;i++) { a[i].flag=read(); a[i].x=read(); if(a[i].flag==2) vis[a[i].x]=1; ans[i]-=2; } for(register int i=1;i<=n;i++) { if(!vis[i]&&Next[i]) { int p=find(Next[i]); if(i==p) father[Next[i]]=-1; father[i]=Next[i]; } } for(register int i=q;i>=1;i--) { if(a[i].flag==1) ans[i]=find(a[i].x); else { int x=a[i].x; int y=find(Next[x]); if(x==y) father[Next[x]]=-1; father[x]=Next[x]; } } for(register int i=1;i<=q;i++) { if(ans[i]==-1) printf("CIKLUS\n"); else if(ans[i]!=-2) put(ans[i]); } return 0; }
既然题目已经给提示 是差分了 所以我们可以往差分的思想上思考 然后我们不妨思考一下
对于这种对原序列的一段区间$l,r$进行+1,-1的操作 加入对于原序列 $a_l,a_r$ 进行+1 我们可以对应到差分序列上 就是$d_l$ 数组 +1 然后 $d_{r+1}$-1 所以对于这种执行区间操作 我
们就转化成了 对差分序列进行两个数字的操作
对于使得最后的数组相同也就是 从差分序列的第2项到第n项 最终变成0的最少操作数 然后我们思考 一下
对于差分序列 我们为了使其变成0 我们可以每次选择一个正数-1 然后选择一个负数+1 然后 我们求出 第二项到第n项的 正数和为 p 负数的绝对值的和为 q
然后 显然操作数就是 $max(p,q)$ 然后 对于 可能的序列数 我们每次讲正数和负数 匹配 然后 我们思考 将其中一个变成0 这样的操作次数是$min(p,q)$
此时序列上剩余 和 为abs(p-q)的数字 然后考虑和 差分数列的$d_1或者d_{n+1}$ 进行配对 然后 我们考虑这样的次数是 abs(p-q)的 然后对于的得到的序列数
我们就是考虑最终 $ d_1 $的所有可能的方案数 就是abs(p-q)+1 因为最后d1可以不变 保持原来的数字
注意开ll 今天我这个同学因为这个爆零好多次qwq
//#include<bits/stdc++.h> #include<iomanip> #include<utility> #include<cctype> #include<vector> #include<deque> #include<map> #include<stack> #include<queue> #include<bitset> #include<set> #include<cstdlib> #include<algorithm> #include<iostream> #include<cstdio> #include<ctime> #include<cmath> #include<cstring> #include<string> #define INF 2147483646 #define up(p,i,n) for(long long i=p;i<=n;i++) using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline void put(long long x) { x<0?x=-x,putchar('-'):0; long long num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const long long MAXN=100002; long long a[MAXN],b[MAXN]; long long n; long long ans=0,cnt=0; int main() { n=read(); up(1,i,n)a[i]=read(); up(2,i,n)b[i]=a[i]-a[i-1]; up(2,i,n){if(b[i]<0)ans+=b[i];if(b[i]>0)cnt+=b[i];} ans=abs(ans);cnt=abs(cnt); put(max(ans,cnt)); put(abs(ans-cnt)+1); return 0; }
我又把背包问题忘完了qwq 先复习一下以前的知识8
今天早上做了一个 01背包的第k优解 其实 这个知识点是很早发现的 但一直没写qwq
我们设$f_{i j k}$表示 前 i 个物品 占了 体积为 j 的背包时 对应第k优解 的最大价值 根据背包的性质 我们显然 优化掉第一维
所以 有用的状态就是 $f_j 和 f_{j-v[i]}$ 但是这个时候 他们不再对应一个个值 而是我们要把他们当成一个序列 开一个数组记录 每次的最优解 直到选够k个
然后考虑 取前面k个 就得到了 第k优解 但是需要注意的是 我们的数组初始化负无穷 然后 $f_{0 1}=0$ 只有这一个是合法状态
#include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } const int N=5010; const int M=210; int k,n,m,v[M],w[M],f[N][60],c[60]; int main() { read(k); read(m); read(n); for(int i=1;i<=n;i++) read(v[i]),read(w[i]); memset(f,0xcf,sizeof(f)); f[0][1]=0; for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { int l=1,r=1,t=0,tmp=0; while(t<k) { if(f[j][l]<f[j-v[i]][r]+w[i]) { c[++tmp]=f[j-v[i]][r]+w[i],r++; } else c[++tmp]=f[j][l],l++; t++; } for(int s=1;s<=k;s++) f[j][s]=c[s]; } } int ans=0; for(int i=1;i<=k;i++) ans+=f[m][i]; printf("%d\n",ans); return 0; }
然后我们考虑 这个题目是让我们不选择 第i个时 装满背包的方案数 啊怎么写啊qwq 暴力暴力qwq
qwq 爆零选手 回来写博客了 显然根据我们回归到背包最原来的状态 $f_j$ 的转移只有两个一个是 $f_j 和 f_{j-v[i]}$ 前者表示 不选择第i个物品 后者表示选择第i个物品
然后我们可以 根据这个进行转移 我们先求出 不考虑限制的方案数 然后考虑 不选择第i个物品 就是我们将当前的方案数减去强制选择i的方案数
当进行下一次转移的时候 我们 再将 减去的累加回来 因为一次就不选择一个 复杂度 (nm) 模数写错 我又爆零了
#include<bits/stdc++.h> using namespace std; template<typename T>inline void read(T &x) { x=0;T f=1,ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} x*=f; } int n,m,v[1010],f[10100]; int main() { //freopen("1.in","r",stdin); read(n); read(m); for(int i=1;i<=n;i++) read(v[i]); f[0]=1; for(int i=1;i<=n;i++) { for(int j=m;j>=v[i];j--) { f[j]+=f[j-v[i]]; f[j]%=1014; } } for(int k=1,j=v[k];j<=m;j++) {//单独处理第一个 f[j]-=f[j-v[k]]; f[j]%=1014; } cout<<(f[m]+1014)%1014<<' '; for(int k=1;k<=n;) { for(int j=m;j>=v[k];j--) { f[j]+=f[j-v[k]]; f[j]%=1014; } k++; for(int j=v[k];j<=m;j++) { f[j]-=f[j-v[k]]; f[j]%=1014; } cout<<(f[m]+1014)%1014<<' '; if(k==n) return 0; } }