Codeforces 1313 D. Happy New Year —— 记忆化搜索+各种优化

This way

题意:

现在有m个人,然后有n个发糖的操作:
l,r表示会在这个区间的每个位置发1颗糖
k表示就算做了所有的操作,每个人手上的糖的数量不超过k颗
如果第i个人拿到了奇数颗糖,他会高兴,否则就不高兴,让你去做这些操作使得高兴的人数最大

题解:

我本来都放弃做这道题了,去网上找题解的时候,那些题解我觉得太烦了,根本看不进去,但凡我看到了一篇好的题解,我就不会自己去写记忆化搜索,然后A掉。
u1s1这道题确实麻烦,在我写的记忆化搜索里面算前三甲了。

首先由于k的范围只有8,对于这种特殊的范围就一定要注意,往往这就是突破口。那么由此我们可以得知,跨过任意一个人的操作不会超过8个,然后n的范围只有1e5,所以我们可以将人离散化之后,状压这个k,枚举每个人上面的操作的状态。注意前面一个人的状态和后一个人的状态时可能有影响的,所以我unordered_map存的是每个人的所有操作的编号,然后再记忆化搜索的时候检查当前人是否受到上一个人的影响。
然后枚举当前人取的操作的状态进行转移。
但是如果是简单的记忆化搜索的话,会T在第58个和第60个,所以我需要加一些优化,首先要让枚举的状态不能有重复,然后有大于两个的相同的线段就可以将后面的删掉。这些我在代码里都有标注。

时间复杂度不算优秀,我也不清楚为什么记忆化搜索会这么慢,是我写丑了吗
Codeforces 1313 D. Happy New Year —— 记忆化搜索+各种优化_第1张图片

#include
using namespace std;
const int N=2e5+5;
unordered_map<int,int>mp[N];//mp[i][j]:第i个人,编号为j的spell的mask
int vis[N][(1<<8)+5];
int cnt[N];//包含第i个人的spell的编号
struct node{
    int l,r,id;
    bool operator< (const node& a)const {
        return l<a.l;
    }
}e[N];
int dp[N][(1<<8)+5],all,b[N*2];
int dfs(int x,int s){
    if(x>all)
        return 0;
    if(~dp[x][s])
        return dp[x][s];
    int ans=0,ns=0,must=(1<<cnt[x])-1;//ns表示上个点选了的内容,must表示这个点可以选的线段,但是上个点没选择的线段需要去掉
    for(auto i:mp[x]){//上一个点确定了的内容就按照它的来
        if(mp[x-1].count(i.first)){
            if(s&(1<<mp[x-1][i.first]))
                ns|=(1<<i.second);
            else
                must^=(1<<i.second);
        }
    }
    for(int i=0;i<8;i++)//优化,因为之后要|ns,所以ns有的部分就不需要
        if((ns&(1<<i))&&(must&(1<<i)))
            must^=(1<<i);
    for(int j=must;~j;j=min(j-1,(j-1)&must)){//优化,只枚举必要的内容
        int ne=j|ns;
        vis[x][ne]=1;
        ans=max(ans,(__builtin_popcount(ne)%2)*(b[x]-b[x-1])+dfs(x+1,ne));
        //if(!j)break;
    }

    dp[x][s]=ans;
    return ans;
}
int main()
{
    memset(dp,-1,sizeof(dp));
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d",&e[i].l,&e[i].r),e[i].l--;
        b[i*2-1]=e[i].l;
        b[i*2]=e[i].r;
    }
    sort(b+1,b+1+n*2);
    all=unique(b+1,b+1+n*2)-b-1;
    sort(e+1,e+1+n);
    int en=0;
    for(int i=1;i<=n;){//优化,有大于两个相同的就删掉
        while(e[i].l==e[i-2].l&&e[i].r==e[i-2].r)
            i++;
        e[++en]=e[i++];
    }
    for(int i=1;i<=en;i++){
        e[i].l=lower_bound(b+1,b+1+all,e[i].l)-b;
        e[i].r=lower_bound(b+1,b+1+all,e[i].r)-b;
        e[i].id=i;
        for(int j=e[i].l+1;j<=e[i].r;j++)
            mp[j][i]=cnt[j]++;
    }
    printf("%d\n",dfs(2,0));
    return 0;
}

你可能感兴趣的:(想法,dfs,dp)