暑假集训日记——7.28(牛客)

本文深入探讨了算法竞赛中的多项关键技术,包括状态压缩DP、前缀和与差分数组的应用、计算几何、单调队列及DP算法在具体问题中的实现。通过详细解析如书本优惠购买、a+b问题、数组阶乘比较、区间函数修改、基站网络布局及密码破解等题目,提供了实用的解题思路和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

筱玛爱阅读
题解:状压dp
1.书的标签随便贴,所以从大到小排序
2.dp[i]表示买前 i 本可以优惠的最大价格
状态转移方程 dp[i]=max(dp[i],dp[j]+a[cnt[i]]); 表示新买了这本书是否可以凑够一个优惠方案,并且不和之前的优惠方案相冲突
dp好难…

#include<bits/stdc++.h>
using namespace std;
const int N=15;
const int M=1<<N;//所有可能存在的促销情况
int n,m,k,now,v,sum;
int a[N+5],cnt[M],dp[M];//cnt[i]记录i在二进制下有几个1
bool vis[M];
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    scanf("%d",&a[i]);
    sort(a+1,a+n+1,greater<int>());
    for(int i=1;i<=m;++i)
    {
        scanf("%d",&k);
        now=0;
        while(k--)//状态压缩
        {
            scanf("%d",&v);
            now|=(1<<(v-1));
        }
        vis[now]=1;
    }
    for(int i=1;i<M;++i)
    cnt[i]=cnt[i&(i-1)]+1;//从i-lowbit(i)转移
    for(int i=0;i<M;++i)
    {
        if(vis[i]) dp[i]=max(dp[i],a[cnt[i]]);
        for(int j=i;j;j=(j-1)&i)//i的子集j    重点
        {
            int x=i^j;//j关于i的补集x   重点
            if(!vis[x])continue;//用x来免第cnt[i]大的价格
            dp[i]=max(dp[i],dp[j]+a[cnt[i]]);
        }
        for(int j=0;j<n;++j)
        dp[i|(1<<j)]=max(dp[i|(1<<j)],dp[i]);//初始化下一个状态
    }
    for(int i=1;i<=n;++i)   sum+=a[i];
    printf("%d\n",sum-dp[(1<<n)-1]);
    return 0;
}

小w的a+b问题
题解:签到题,然后我不会…
注意当且仅当 -1 时不能被构造

#include<bits/stdc++.h>
using namespace std;
long long c;
int main()
{
    while(scanf("%lld",&c)!=EOF)
    {
        ~c?printf("%lld %lld\n",2147483647LL,2147483649LL+c):printf("No solution\n");
    }
}

小w的a=b问题
题解:
法一:将数组中的数的阶乘中,相同数的出现的次数记录下来,然后将每一个出现过的数分解为质因数,然后比较两个数组的质因数是否完全相同。

法二:上一个方法不直接计算阶乘和乘积和的原因是,数字太大会爆long long
因此采用模计算的话就可以计算出两个数组的值,但是模计算是有误差的,难免会出现虽然实际数值不同,但取模后数值相同的情况,所以为了避免误差,使得结果尽量准确,因此采用不同的模计算,如果所有的模运算结果都相等那么则两数组相等。
注意:模计算取素数,因此所得hash值不会冲突

法二代码:(标程)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long mod[10]={10000019,10000079,10000103,10000121,10000139,10000141,10000169,10000189,10000223,10000229};
long long fact[MAXN][10],hash1[10],hash2[10],a[MAXN],b[MAXN];
int T,n,m;
int main()
{
    for(int i=0;i<10;++i)
    {
        fact[0][i]=1;
    }
    for(long long i=1;i<=100000;++i)
    {
        for(int j=0;j<10;++j)
        {
            fact[i][j]=(fact[i-1][j]*i)%mod[j];
        }
    }
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=m;++i)
        {
            scanf("%lld",&b[i]);
        }
        for(int i=1;i<10;++i)
        {
            hash1[i]=hash2[i]=1;
        }
        for(int i=1;i<=n;++i)
        {
            for(int j=0;j<10;++j)
            {
                hash1[j]=(hash1[j]*fact[a[i]][j])%mod[j];
            }
        }
        for(int i=1;i<=m;++i)
        {
            for(int j=0;j<10;++j)
            {
                hash2[j]=(hash2[j]*fact[b[i]][j])%mod[j];
            }
        }
        bool flag=true;
        for(int j=0;j<10;++j)
        {
            if(hash1[j]!=hash2[j])
            {
                flag=false;
            }
        }
        if(flag)
        {
            printf("equal\n");
        }
        else
        {
            printf("unequal\n");
        }
    }
    return 0;
}

题解:差分维护+前缀和(重点如何在一个区间维护不同的值,典型区间修改
先离线,这次利用前缀和与差分数组的关系。

我们知道区间加常数c可以先做差分,此时操作变为单点修改,然后使用前缀和还原。

区间加一次函数bx呢,则可以做二阶差分操作(先差分一次,再对差分数组做一次差分)这时区间加一次函数bx就变为了单点修改,然后做二阶前缀和(前缀和的前缀和)还原,然后这个题就做完了。

区间加二次函数ax^2 以此类推可以做三阶差分,也就是做差分,然后再差分,再差分。最后做三次前缀和还原。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
const long long mod=(long long)1e9+7;
long long d1[MAXN],d2[MAXN],d3[MAXN];//1 id id2
int n,m,T,type,pos;
void pre_sum(long long a[])
{
    for(int i=1;i<=n;++i)
    {
        a[i]=(a[i]+a[i-1])%mod;
    }
    return;
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        memset(d1,0,sizeof(d1));
        memset(d2,0,sizeof(d2));
        memset(d3,0,sizeof(d3));
        scanf("%d %d",&n,&m);
        while(m--)
        {
            scanf("%d %d",&type,&pos);
            if(type==1)
            {
                d1[pos]++;
            }
            if(type==2)
            {
                d2[pos]++;
            }
            if(type==3)
            {
                d3[pos]++;
                d3[pos+1]++;
            }
        }
        pre_sum(d1);
 
        pre_sum(d2);
        pre_sum(d2);
 
        pre_sum(d3);
        pre_sum(d3);
        pre_sum(d3);
 
        for(int i=1;i<=n;++i)
        {
            printf("%lld%c",(d1[i]+d2[i]+d3[i])%mod,i==n?'\n':' ');
        }
    }
    return 0;
}

小w的基站网络(未解决)
题解:计算几何+单调队列+dp

标程:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1000005;
struct vect
{
    long long x;
    long long y;
    int ki;
}a[MAXN];
bool cmp(const vect &x,const vect &y)
{
    long long temp=x.x*y.y-x.y*y.x;
    if(temp)
    {
        return temp>0;
    }
    else
    {
        return x.x*x.x+x.y*x.y>y.x*y.x+y.y*y.y;
    }
}
long long Cross(vect x,vect y)
{
    return x.x*y.y-x.y*y.x;
}
int bgin,s;
long long dp[MAXN];
int stk[MAXN],top,n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%lld %lld",&a[i].x,&a[i].y);
        a[i].ki=i;
    }
    sort(a+1,a+1+n,cmp);
    scanf("%d",&s);
    for(int i=1;i<=n;++i)
    {
        if(a[i].ki==s)
        {
            bgin=i;
            break;
        }
    }
    memset(dp,-1,sizeof(dp));
    dp[s]=0;
    stk[top=1]=bgin;
    for(int i=bgin+1;i<=n;++i)
    {
        if(Cross(a[bgin],a[i])==0)continue;
        while(top>1&&Cross(a[stk[top-1]],a[stk[top]])==0)--top;
        while(top>1&&dp[a[stk[top]].ki]+Cross(a[stk[top]],a[i])>=dp[a[stk[top-1]].ki]+Cross(a[stk[top-1]],a[i]))--top;
        dp[a[i].ki]=dp[a[stk[top]].ki]+Cross(a[stk[top]],a[i]);
        stk[++top]=i;
    }
    for(int i=1;i<=n;++i)
    {
        printf("%lld\n",dp[i]);
    }
    return 0;
}

DongDong破密码
题解:直接模拟即可
根据样例分析,所求串为a,密码为b
a1 ^ 0 = b1
a2 ^ a1=b2
a3 ^ a2 ^a1=b3
a4 ^ a3 ^a2=b4
a5 ^ a4 ^a3=b5
a6 ^ a5 ^a4=b6
因此求a串的关系式为:
a1^0 ^0= a1 =b1 ^0
a2 ^a1 ^a1= a2 =b2 ^a1=b2 ^b1
a3 ^ a2 ^a1 ^ a2 ^a1= a3 =b3 ^ a2 ^a1=b3 ^b2
a4 ^ a3 ^a2 ^ a3 ^a2= a4 =b4 ^ a3 ^a2=b4 ^b3 ^a1 (1=4-3)
…等

#include <algorithm>
#include <cstring>
#include <cstdio>
 
using namespace std ;
 
const int N=2e6+10 ;
 
char s[N] ;
int a[N] , b[N] ;
int n , m ;
 
int main ()
{
    int i ;
    scanf(" %d %d %s ",&n,&m,s+1);
    for ( i=1 ; i<=n+m-1 ; i++ ) a[i]=(s[i]=='1');
    for ( i=1 ; i<=n ; i++ )
        if ( i<=m ) b[i]=a[i]^a[i-1];
        else b[i]=a[i]^a[i-1]^b[i-m];
    for ( i=1 ; i<=n ; i++ ) printf("%d",b[i]);
    return 0 ;
}

DongDong跳一跳
题解:dp
一开始想直接暴力求解 但复杂度太大 O(n^2)
然后考虑优化,记录之前达到此高度的柱子所能获得的最大鱼干数量,然后在动态转移 复杂度为 O(n*m) m的值比n小很多,复杂度不高

#include<bits/stdc++.h>
#define mp make_pair
using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;


const int N=1e7+10;
const int MAXN=20010;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;

ll a[N],h[N],dp[N],tong[N];

int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>h[i]>>a[i];
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        ll maxx=0;
        for(int j=max(0ll,h[i]-m);j<=h[i]+m;j++)
        {
            maxx=max(maxx,tong[j]);
        }
        dp[i]=maxx+a[i],tong[h[i]]=max(tong[h[i]],dp[i]),ans=max(ans,dp[i]);
    }
    cout<<ans<<endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值