UNR #4

序列妙妙值

题目描述

点此看题

解法

我真的感觉这题并不简单,但是好像大家都会的样子

dp[i][j] 表示前 i 个数划分成 j 段的最小代价,设 si=j=1iaj,转移:

dp[i][j]dp[k][j1]+(sisk)

这个转移虽然形式简洁,但是没有什么性质也难以直接套数据结构。不妨先考虑 ai<28 的部分分,可以维护 28 个桶,每个桶表示这个 s 值对应最小的 dp[k][j1],时间复杂度 O(nkv)

考虑把上面的做法推广到 ai<216,自然想到值域分块,目的是平衡修改的查询的时间(原来是 O(1)O(v) 的,尝试优化成 O(v)O(v)),维护 f[u][v] 表示假设 si 的前 8 位为 u,已知 sj 的后 8 位为 v,最小的代价是多少(注意前 8 位代表较大的数位,前 8 位的代价在修改时放进 f 中,后 8 位的代价在查询时统计)

时间复杂度 O(nkv)写完博客突然感觉这题在哪里见过

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 60005;
const int inf = 0x3f3f3f3f;
int read()
{
    int x=0,f=1;char c;
    while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return x*f;
}
int n,k,a[M],dp[M][10],f[1<<8][1<<8];
void upd(int &x,int y) {x=min(x,y);}
int main()
{
    n=read();k=read();
    for(int i=1;i<=n;i++)
        a[i]=read()^a[i-1];
    memset(dp,0x3f,sizeof dp);
    dp[0][0]=0;
    for(int j=1;j<=k;j++)
    {
        for(int i=0;i<(1<<16);i++)
            f[i>>8][i&255]=inf;
        for(int i=j;i<=n;i++)
        {
            for(int k=0;k<(1<<8);k++)
            {
                int x=(k^(a[i-1]>>8))<<8;
                upd(f[k][a[i-1]&255],dp[i-1][j-1]+x);
            }
            for(int k=0;k<(1<<8);k++)
                upd(dp[i][j],f[a[i]>>8][k]+((a[i]^k)&255));
        }
    }
    for(int i=k;i<=n;i++)
        printf("%d ",dp[i][k]);
}

网络恢复

题目描述

点此看题

解法

交互题一定要注意特殊性质的部分分,很多时候有一个特殊性质的做法可以直接推广到正解。

考虑树的部分分,从叶子往上确定整棵树。那么我们给每个点随机一个 264 以内的权值,如果点 ubu 是某个 vav,那么可以认为 u 是叶子且 (u,v) 直接有边相连,把 u 删除之后循环这个过程,只消耗一次询问。

上面的做法其实只要无环就可以求出原图的所有边,那么考虑把原图的边划分为若干个无环的子图,对这些子图分别用树的方法就可以得到答案。具体来说我们循环一下过程 10 次:随机一个边的排列,把这个排列 5 等分,就获得了 5 个子图,注意一条边可能被确定多次,需要判断一下,实测可以获得 70 分。

边数很多时构成环的概率很大,但是由于数据随机的原因,很难有导出子图满足每个点度数都 3,所以我们只需要支持在不存在叶子时,确定某个度数 =2 的点的边即可。可以把所有点都拿出来,然后随机两个点,假设它们之间有边然后断掉,如果此时出现了真的叶子,那么可以判定它们真的有边,就达到了判断环的目的。

所以我们把原图的边直接 50 等分,然后魔改一下树的做法使其可以支持一些简单环的拆解,可以获得 100 分。

#include "explore.h"
#include <bits/stdc++.h>
#include <bits/extc++.h>
using namespace std;
#define vc vector
#define ull unsigned long long
const int M = 50005;
//
int n,m,k,h[M],q[M];vc<ull> a,b;
__gnu_pbds::gp_hash_table<ull,int> id;
__gnu_pbds::gp_hash_table<int,int> e[M];
mt19937_64 X(time(0));mt19937 Y(time(0));
void report(int x,int y)
{
    if(x>y) swap(x,y);
    if(!e[x][y]) Report(x+1,y+1);
    e[x][y]=1;
    b[x]^=a[y];b[y]^=a[x];
    if(id[b[x]]) q[++k]=x;
    if(id[b[y]]) q[++k]=y;
}
void work(int l,int r)
{
    vc<int> t;
    for(int i=l;i<=r;i++)
        t.push_back(i);
    b=Query(a,t);
    for(int i=0;i<n;i++)
        if(id[b[i]]) q[++k]=i;
    while(1)
    {
        if(k)
        {
            int u=q[k--];
            if(b[u]) report(u,id[b[u]]-1);
        }
        else
        {
            int l=0;
            for(int i=0;i<n;i++)
                if(b[i]) h[l++]=i;
            if(!l) return ;
            while(1)
            {
                int x=rand()%(l-1)+1,y=rand()%x;
                x=h[x];y=h[y];
                if(id[b[x]^a[y]] || id[b[y]^a[x]])
                    {report(x,y);break;}
            }
        }
    }
}
void Solve(int n1,int m1)
{
    n=n1;m=m1;
    for(int i=0;i<n;i++)
        a.push_back(X()),id[a[i]]=i+1;
    int bk=(m+49)/50;
    for(int i=1;i<=50;i++)
        work(i*bk-bk+1,min(i*bk,m));
}
posted @   C202044zxy  阅读(394)  评论(10编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2021-08-09 [做题笔记] 浅谈状压dp在图计数问题上的应用
2021-08-09 [2017 山东一轮集训 Day7] 逆序对
点击右上角即可分享
微信分享提示