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;
}

 

posted @ 2017-09-26 20:06  橙子用户  阅读(112)  评论(0编辑  收藏  举报