3.16做题记录

P5358 [SDOI2019]快速查询

整体打标记,注意顺序即可


代码

P5361 [SDOI2019]热闹的聚会与尴尬的聚会

第一问导出子图的最小点度数尽可能大§:每次删去度数最小的点,更新其它点的度数,找到剩下所有人点度数的最小值最大的一个位置

第二问尽可能大的独立集(q):每次选出度数最小的点,删去它和它相连的点,然后继续选度数最小的,直到没得选

这种是求最大独立集(NPC问题)的一种近似算法,这道题目中可以证 ( p + 1 ) ( q + 1 ) ≥ n + 1 (p+1)(q+1)\ge n+1 (p+1)(q+1)n+1,因为求独立集的时候每次删掉的点的度数+1之和为n,那么最大值+1乘上次数一定大于n


代码

P5363 [SDOI2019]移动金币

把两个金币之间的空格数当成放在一个台阶上的石子个数,那么左移一个金币相当于把若干个石子向下移动一个台阶

那么就是经典的阶梯博弈了,后手必胜的局面是偶数编号的台阶上石子个数异或和为0,题目问存在多少中这样的初始状态

因为直接dp复杂度过高,因为有异或操作,考虑二进制每一位的情况,如果偶数位置异或和为0,意味着二进制下每一位,偶数位置都有偶数个1,这样我们就可以dp了,设 f i , j f_{i,j} fi,j表示二进制下从高位到低位计算到了第 i 位,剩余石子 j 个的方案数,我们可以枚举 2k 表示放 2k 个石子在偶数位置上转移即可

剩下的石子放在奇数位置,利用插板法计算即可


代码

ybtoj761. 「启发式合并」最长路径

利用线段树下标表示深度,值表示到根的距离
每次先计算轻儿子,清空线段树,然后计算重儿子不清空线段树,再走一次轻儿子计算贡献并加入到线段树中

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,ll> PIL;
const int maxn=1e6+5;
const ll inf=1e17;
const ll mod=998244353;
int n,ql[maxn],qr[maxn];
vector <PIL> G[maxn];
int siz[maxn],son[maxn];
int mxdep[maxn],dep[maxn];
ll dis[maxn];
void dfs(int u,int fa)
{
    siz[u]=1; mxdep[u]=dep[u]=dep[fa]+1;
    for(auto to:G[u])
    {
        int v=to.first;
        dis[v]=dis[u]+to.second;
        dfs(v,u);
        siz[u]+=siz[v];
        if(siz[son[u]]<siz[v]) son[u]=v;
        mxdep[u]=max(mxdep[u],mxdep[v]);
    }
}
ll tr[maxn<<2];
#define lson now<<1
#define rson now<<1|1
void change(int now,int l,int r,int pos,ll val)
{
    if(l==r)
    {
        tr[now]=val;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) change(lson,l,mid,pos,val);
    else change(rson,mid+1,r,pos,val);
    tr[now]=max(tr[lson],tr[rson]);
}
void update(int now,int l,int r,int pos,ll val)
{
    if(l==r)
    {
        tr[now]=max(tr[now],val);
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) update(lson,l,mid,pos,val);
    else update(rson,mid+1,r,pos,val);
    tr[now]=max(tr[lson],tr[rson]);
}
ll query(int now,int l,int r,int L,int R)
{
    if(l>=L && r<=R) return tr[now];
    int mid=l+r>>1;
    ll res=-inf;
    if(L<=mid) res=max(res,query(lson,l,mid,L,R));
    if(mid<R) res=max(res,query(rson,mid+1,r,L,R));
    return res;
}
ll ans[maxn];
void calc(int u,int fa)
{
    ans[fa]=max(ans[fa],query(1,1,n,max(1,ql[fa]-dep[u]+2*dep[fa]),min(n,qr[fa]-dep[u]+2*dep[fa]))+dis[u]-2*dis[fa]);
    for(auto to:G[u])
        calc(to.first,fa);
}
void add(int u)
{
    update(1,1,n,dep[u],dis[u]);
    for(auto to:G[u])
        add(to.first);
}
void solve(int u)
{
    for(auto to:G[u])
    {
        int v=to.first;
        if(v==son[u]) continue;
        solve(v);
        for(int i=dep[v];i<=mxdep[v];i++)
            change(1,1,n,i,-inf);
    }
    if(son[u]) solve(son[u]);
    ans[u]=max(ans[u],query(1,1,n,max(1,dep[u]+ql[u]),min(n,dep[u]+qr[u]))-dis[u]);
    update(1,1,n,dep[u],dis[u]);
    for(auto to:G[u])
    {
        int v=to.first;
        if(v==son[u]) continue;
        calc(v,u); add(v);
    }   
}
int main()
{
    freopen("watchdog.in","r",stdin);
    freopen("watchdog.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d%d",&ql[i],&qr[i]),ans[i]=-1;
    int x; ll z;
    for(int i=2;i<=n;i++)
    {
        scanf("%d%lld",&x,&z);
        G[x].push_back(make_pair(i,z));
    }
    dfs(1,0);
    for(int i=1;i<=(n<<2);i++) tr[i]=-inf;
    solve(1);
    for(int i=1;i<=n;i++) ans[i]=(ans[i]%mod+mod)%mod;
    ll res=0,bas=1;
    for(int i=n;i;i--)
        res=(res+bas*ans[i]%mod)%mod,bas=bas*23333ll%mod;
    printf("%lld",res);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值