XJOI contest800
第一题
题目描述:
数据范围:
0<=n, m<=10^5.
具体思路:树状数组
开两个,一个记录左端点,另一个记录右端点,然后答案就是总数减去左端点大于r的和右端点小于l的
AC代码
#include <cstdio> #include <cstring> const int MAXN=100010; int n,m; int a[2][MAXN],q,l,r,tot; int lowbit(int x){return x&(-x);} void add(int k,int x) { while (x<=n)a[k][x]++,x+=lowbit(x); } int sum(int k,int x) { int ans=0; while (x>=1)ans+=a[k][x],x-=lowbit(x); return ans; } int main() { scanf("%d%d",&n,&m);n++; for (int i=1;i<=m;i++) { scanf("%d%d%d",&q,&l,&r); if (q==1)add(0,l),add(1,r),tot++; else printf("%d\n",sum(0,r)-sum(1,l-1)); } }
第二题
题目描述:
数据范围:
总人数 K<=50。
每个背包的容量 V<=5000。
物品种类数 N<=200。
其它正整数都不超过 5000。
输入数据保证存在满足要求的方案。
具体思路:
这不一眼DP题嘛
还是01背包呢
但难在要求出最优的k个解
sof[i][j]表示容量为i的包的j优解
然后转移的时候就是两个合并一下,保留前k个
AC代码
#include<bits/stdc++.h> using namespace std; int v[5010],w[5010],f[5010][60],t[5010]; int nowj,nowjv,i,j,k,n,vmx,num,h,m,sum,sum1,sum2,total; int main() { scanf("%d%d%d",&k,&vmx,&n); for (i=1;i<=n;i++) scanf("%d%d",&v[i],&w[i]); for (i=0;i<=vmx;i++) { for (j=1;j<=k;j++) f[i][j]=-100000000; f[i][0]=0; } f[0][0]=1; f[0][1]=0; for (i=1;i<=n;i++) { for (j=vmx-v[i];j>=0;j--) { for (h=1;h<=f[j+v[i]][0];h++)t[h]=f[j+v[i]][h]; sum1=f[j][0]; sum2=f[j+v[i]][0];sum=sum1+sum2; if (sum>k) sum=k; f[j+v[i]][0]=sum; nowj=1,nowjv=1; for (h=1;h<=sum;h++) { if (((f[j][nowj]+w[i]>t[nowjv]) &&(nowj<=sum1)) ||(nowjv>sum2)) f[j+v[i]][h]=f[j][nowj]+w[i],nowj++; else f[j+v[i]][h]=t[nowjv],nowjv++; } } } total=0; for (i=1;i<=f[vmx][0];i++) total+=f[vmx][i]; printf("%d",total); }
第三题
题目描述:
数据范围:
药水种类 N<=60。
配制方法数 M<=240。
初始的金币数 V<=1000。
每天可施的魔法数 K<=30。
具体思路:今天DP题很多啊
这个题要三次次dp
f[i][j]表示用i次膜法造出j号膜药的最小花费
然后每次转移也是一次dp(就是这个配制膜药的方法的每个原料共用i-1次膜法的最小花费)
然后狂扫一下f[i][j]把有意义的方案存下来(就是用的金币比直接买少的)
最后dp[i][j]表示用i次膜法,j个金币,可以得到的最大利润
AC代码
#include<bits/stdc++.h> using namespace std; const int N=65,M=345,V=1010,K=35; vector <int> s[1000]; int n,m,v,k,g[400][800],sell[1000],buy[1000],x,y,tf[800][400],z; int top,w[200000][4],f[80][2000]; int calc(vector <int>&a,int k) { int n=a.size()-1; for (int i=0;i<=n;++i) for (int j=0;j<=k;++j) tf[i][j]=1e5; tf[0][0]=0; for (int i=1;i<=n;++i) for (int j=0;j<=k;++j) for (int l=0;l<=j;++l) tf[i][j]=min(tf[i][j],tf[i-1][j-l]+g[l][a[i]]); return tf[n][k]; } int main() { scanf("%d%d%d%d",&n,&m,&v,&k); for (int i=1;i<=n;i++)scanf("%d%d",&sell[i],&buy[i]); for (int i=1;i<=m;i++) { scanf("%d%d",&x,&y); s[i].push_back(x); for (int j=1;j<=y;j++)scanf("%d",&z),s[i].push_back(z); } memset(g,0x3f,sizeof(g)); for (int i=1;i<=n;i++)g[0][i]=sell[i]; for (int i=1;i<=k;i++) for (int j=1;j<=m;j++) g[i][s[j][0]]=min(g[i][s[j][0]],calc(s[j],i-1)); for (int i=0;i<=k;i++) for (int j=1;j<=n;j++) if(g[i][j]<=buy[j])top++,w[top][0]=i,w[top][1]=g[i][j],w[top][2]=buy[j]-g[i][j]; int ans=0; for (int i=0;i<=k;i++) for (int j=0;j<=v;j++) { for (int hh=1;hh<=top;hh++) if((i-w[hh][0])>=0&&(j-w[hh][1])>=0) f[i][j]=max(f[i][j],f[i-w[hh][0]][j-w[hh][1]]+w[hh][2]); ans=max(ans,f[i][j]); } printf("%d",ans); return 0; }