2022杭电多校#2

杭电多校2

1004 I love counting

思路:用莫队处理每次询问,并将a[i]对应的纵坐标进行分块,用num数组记录每个a[i]的出现的个数,用sum表示每个块里面不为0的数的个数。对于每个j,求出小于他的所有值,最后加上异或后等于b的情况;

代码:

#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;

const int maxn = 1e6+10;

int n,q;
int siz,len;
int a[maxn];
int belong[maxn];
int now;
int ans[maxn];
int num[maxn],sum[maxn];
int k;

struct node
{
    int l,r,a,b,id;
}e[maxn];

bool cmp(node a,node b)
{
    return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);//处理查询的排序方法
}

void add(int x)
{
    if(++num[x]==1)sum[x/k]++;
}

void del(int x)
{
    if(--num[x]==0)sum[x/k]--;
}

int calc(int x)
{
    int ans=0;
    for(int i=0;i<x/k;i++)
        ans+=sum[i];
    for(int i=(x/k)*k;i<=x;i++)
        ans+=(num[i]!=0);
    return ans;
}



int main()
{
    scanf("%d",&n);
    k=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
    }
    siz=sqrt(n);
    len=ceil((double)n/siz);
    for(int i=1;i<=siz;i++)
    {
        for(int j=1;j<=len;j++)
        {
            belong[j+(i-1)*len]=i;
        }
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d%d",&e[i].l,&e[i].r,&e[i].a,&e[i].b);
        e[i].id=i;
    }
    sort(e+1,e+1+q,cmp);
    int l=1,r=0;

    for(int i=1;i<=q;i++)
    {
        int s=0;
        int p=0;
        int ql=e[i].l;
        int qr=e[i].r;
        while(l<ql)del(a[l++]);
        while(l>ql)add(a[--l]);
        while(r>qr)del(a[r--]);
        while(r<qr)add(a[++r]);
        int a=e[i].a;
        int b=e[i].b;
        for(int j=19;j>=0;j--)
        {
            p=s;
            if((b>>j)&1)
            {
                if((a>>j)&1)
                {
                    p|=(1<<j);
                }else
                {
                    s|=(1<<j);
                }
                ans[e[i].id]+=calc(p+(1<<j)-1)-calc(p-1);
            }else
            {
                if((a>>j)&1)
                {
                    s|=(1<<j);
                }
            }
        }
        ans[e[i].id]+=(num[e[i].a^e[i].b]!=0);
    }
    for(int i=1;i<=q;i++)
    {
        printf("%d\n",ans[i]);
    }
}



1008 I love exam

思路:先将每个学科分开,看作单个背包,算出每个背包各个容量下的最大价值,主要要保证分数不能超过100。
然后我们需要将各学科合并。

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <vector>
#include <stack>
#include <queue>
#include <map>
#include <set>
#include <utility>
#define pi 3.1415926535898
#define ll long long
#define lson rt<<1
#define rson rt<<1|1
#define eps 1e-6
#define ms(a,b) memset(a,b,sizeof(a))
#define legal(a,b) a&b
#define print1 printf("111\n")
#define pb(x) push_back(x)
#define pair4 pair<pair<int,int>,pair<int,int> >
#define fi first
#define se second
using namespace std;
const int maxn = 3e5+10;
const int inf = 1e9+10;
const ll llinf =1e18+10;
const ll mod = 1e9+7;

int n,m,cnt,s,p;
string a;
map<string,int>mp;
vector<vector<pair<int,int> > >vec(15000+10);
int f[100][15000+10];
int dp[100][15000+10][5];

int main()
{
    int _;
    scanf("%d",&_);

    while(_--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            cin>>a;
            mp[a]=i;
        }
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            int u,v;
            cin>>a>>u>>v;
            vec[mp[a]].push_back(make_pair(u,v));
        }
        scanf("%d%d",&s,&p);
        ms(f,-inf);

        for(int i=1;i<=n;i++)
        {
            f[i][0]=0;
            for(int j=0;j<vec[i].size();j++)
            {
                for(int z=s;z>=0;z--)
                {
                    int w=vec[i][j].se;
                    int v=vec[i][j].fi;
                    if(z>=w)
                        f[i][z]=max(min(100,f[i][z-w]+v),f[i][z]);

                }
            }

        }

        ms(dp,-inf);
        dp[0][0][0]=0;
        for(int i=1;i<=n;i++)//第i门
        {
            for(int j=0;j<=s;j++)//花费j时间
            {
                for(int t=0;t<=s;t++)//背包容量
                {
                    for(int z=0;z<=min(i,p);z++)//挂科数目
                    {
                        int x=0;
                        if(f[i][j]<60)
                        {
                            x=1;
                        }
                        if(t>=j&&x==0)
                            dp[i][t][z]=max(dp[i][t][z],dp[i-1][t-j][z]+f[i][j]);
                        else if(t>=j&&z>0)
                            dp[i][t][z]=max(dp[i][t][z],dp[i-1][t-j][z-x]+f[i][j]);
                    }
                }
            }
        }
        int ma=-1;
        for(int i=0;i<=s;i++)
        {
            for(int j=0;j<=p;j++)
            {
                ma=max(ma,dp[n][i][j]);
            }
        }
        printf("%d\n",ma);
        mp.clear();
        for(int i=1;i<=n;i++)vec[i].clear();
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值