Problem - 1633D - Codeforces,C - Klee in Solitary Confinement,H - Crystalfly 树形d

60 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情

Problem - 1633D - Codeforces

先求出1转化为每一个数需要的最小次数f[b[i]],问题就转化为容量为k,每件物品的体积为f[b[i]],价值为c[i],01背包,做这题的时候脑子抽了以为只有因数才能转移,但是显然不是,比如86也可以去加9,因为86/9=9,,,做题还是要仔细一点

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 3e5+10;
int t,n,k,b[1005],c[1005],f[1005],dp[1000006],w[1005],y[1005];
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    for(int i=1;i<=1000;i++) f[i]=1e18;
    f[1]=0;
    int maxx=0;
    map<int,int>mp;
    for(int i=1;i<=1000;i++)
    {
        for(int j=1;j<=i;j++){
            int x=i/j;
            if(i+x<=1000) f[i+x]=min(f[i+x],f[i]+1);
        }
    }
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        k=min(12000LL,k);
        for(int i=0;i<=k;i++) dp[i]=0;
        for(int i=1;i<=n;i++) cin>>b[i];
        for(int i=1;i<=n;i++) cin>>c[i];
        for(int i=1;i<=n;i++)
        {
            for(int j=k;j>=f[b[i]];j--)
            dp[j]=max(dp[j],dp[j-f[b[i]]]+c[i]);
        }
        cout<<dp[k]<<endl;
    }
    system("pause");
    return 0;
}

C - Klee in Solitary Confinement 

考虑每一个数x和x+k,对于x+k来说,一段区间里含的x+k越少越好,x越多越好,那么这个区间就可以都加上k;tx[x]是x在序列中的出现次数,cnt[x]是在指定的操作区间内x可以增加的个数,可以想一下假定是lr这个区间,那么遇到x我们就让cnt[x+k]++,说明我们可以让这个数加上k,然后cnt[x]=max(cnt[x],0),这句话是对于x-k和x来说的,因为这个lr区间内都加上了k,那么(x-k)+k=x,但是x+k就会让x的出现次数减少了,也就是增量会-1,但是增量最少也会是0,不会比原先更少,所以要和0比较一下,总之就是模拟实现了一下l,r这个区间内x-k的个数为a,x的个数为b,那么x可以增加的个数就是max(a-b,0);这样就可以线性的去遍历去求最大值了

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 4e6+10;
const int B=2e6;
int n,k,a[N],tx[N],cnt[N];
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>n>>k;
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];a[i]+=B;
        tx[a[i]]++;
        ans=max(ans,tx[a[i]]);
    }
    for(int i=1;i<=n;i++)
    {
        cnt[a[i]+k]++;
        cnt[a[i]]=max(0LL,cnt[a[i]]-1);
        ans=max(ans,tx[a[i]+k]+cnt[a[i]+k]);
    }
    cout<<ans<<endl;
    system("pause");
    return 0;
}

H - Crystalfly 树形dp

f[i]表示选了i节点然后继续去走i的子节点可以收获的最大值,g[i]表示没选i节点然后继续去走i的子节点可以收获的最大值,h[i]表示选了i节点但是没有选i的子节点继续向下走可以收获的最大值;

那么可以分成两种情况:

第一种:t[i]没有等于3的,那么就是找一个最大的节点a[i],然后所有的g[i]都可以得到;

第二种:有一个节点u可以额外的拿到,但是必须返回的点是t[i]=3才可以,这样的话就是h[u]+sum(g(不包括u这个点))+max(a[i](不包括u)),这样找出a[i]的次大值和最大值,就可以o(n)的来枚举了

2021 ICPC 南京 H-Crystalfly(树上DP) - FJ-Frank - 博客园 (cnblogs.com)

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int inf=1e18;
const int N = 4e6+10;
const int B=2e6;
int T,n,a[100005],t[100005],f[100005],g[100005],h[100005];
int head[200005],cnt;
struct Edge
{
    int next,to;
}e[200005];
void addedge(int from,int to)
{
    e[++cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
void dfs(int u,int fa)
{
    h[u]=a[u];
    int res=0,max1=0,max2=0,maxx=0;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa) continue;
        dfs(v,u);
        res+=g[v];
        h[u]+=g[v];
        maxx=max(maxx,a[v]);
        if(t[v]==3)
        {
            if(max1<a[v]) max2=max1,max1=a[v];
            else if(max2<a[v]) max2=a[v];
        }
    }
    g[u]=maxx+res;
    for(int i=head[u];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa) continue;
        if(a[v]!=max1||t[v]!=3) g[u]=max(g[u],res-g[v]+h[v]+max1);
        else g[u]=max(g[u],res-g[v]+h[v]+max2);
    }
    f[u]=a[u]+g[u];
}
signed main()
{
    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);
    cin>>T;
   while(T--)
   {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=1;i<=n;i++) cin>>t[i];
        for(int i=1;i<n;i++)
        {
            int u,v;
            cin>>u>>v;
            addedge(u,v);
            addedge(v,u);
        }
        dfs(1,0);
        cout<<f[1]<<endl;
        for(int i=1;i<=2*n;i++) head[i]=0,e[i].next=e[i].to=0;
        cnt=0;
   }
    system("pause");
    return 0;
}

J - Xingqiu's Joke 记忆化搜索,数学

c是a,b的差,那么a,b的公共素因子也会是c的素因子,但反过来好像就不是了;

能除素因子就除素因子,可以先求出c的素因子,然后记忆化搜索,其实a和b只要最小的一个作为参数就可以了,因为他们的操作是一样的,最小的到1了就会满足条件了,假设a是最小的,如果能a能整除c的素因子d就求出a除以d下取整所需要的步数tmp1,a除以d上取整所需要的步数tmp2,以及a直接减到1所需要的步数res,最后遍历完所有因子求出一个最小的就是当前的a和c所需要的最小步数;

J. Xingqiu's Joke_哔哩哔哩_bilibili

int t;
vector<int>v;
void pri(int c)
{
    int x=c;
    for(int i=2;i*i<=c;i++)
    {
        if(x%i==0)
        {
            v.push_back(i);
            while(x%i==0) x/=i;
        }
    }
    if(x>1) v.push_back(x);
}
map<pair<int,int>,int>dp;
int dfs(int a,int c)
{
    if(a==1) return 0;
    if(c==1) return a-1;
    if(dp[{a,c}]) return dp[{a,c}];
    int res=a-1;
    for(auto d:v)
    {
        if(c%d==0)
        {
            int rest=a%d;
            int tmp1=rest+1+dfs(a/d,c/d);
            int tmp2=(d-rest)+1+dfs(a/d+1,c/d);
            res=min({res,tmp1,tmp2});
        }
    }
    return dp[{a,c}]=res;
}
signed main()
{
    // cin.tie(0);
    // cout.tie(0);
    // ios::sync_with_stdio(0);
    cin>>t;
    while(t--)
    {
        int a,b,c;
        dp.clear();
        v.clear();
        cin>>a>>b; 
        c=abs(a-b);
        pri(c);
        int ans=dfs(min(a,b),c);
        cout<<ans<<endl;
    }
    system("pause");
    return 0;
}

1486C1 - Guessing the Greatest (easy version) 交互+二分

一开始l=1,r=n,先询问l,r的次大值的位置x,然后看看x是在l,mid之间还是mid到r之间,假设在l,mid之间,然后再去询问一遍x所在的区间,看看新的l,mid次大值的位置y是否等于x,如果不等于说明最大值没有在这半个区间内,这个区间就没有用了直接l=mid+1,否则就让r=mid,因为mid这个位置上可能是最大值所以不能丢,一直二分到l==r这样就可以直接输出答案,也可能最后是l==r-1的情况,这样单独判一边得出答案break掉就可以了

#include <bits/stdc++.h>
#define int long long
#define lowbit(x) ((x)&(-x))
#define endl '\n'
using namespace std;
const int N = 4e6+10;
const int B=2e6;
int n;
signed main()
{
    // cin.tie(0);
    // cout.tie(0);
    // ios::sync_with_stdio(0);
    cin>>n;
    int l=1,r=n,ans=0;
    while(l<r)
    {
        cout<<"? "<<l<<" "<<r<<endl;
        cout.flush();
        int x;
        cin>>x;
        if(l==r-1)
        {
            if(x==l) ans=r;
            else ans=l;
            break;
        }
        int mid=l+r>>1;
        if(x>=l&&x<=mid)
        {
            cout<<"? "<<l<<" "<<mid<<endl;
            cout.flush();
            int y;
            cin>>y;
            if(x!=y) l=mid+1;
            else r=mid,ans=mid;
        }
        else
        {
            cout<<"? "<<mid<<" "<<r<<endl;
            cout.flush();
            int y;
            cin>>y;
            if(x!=y) r=mid-1;
            else l=mid,ans=mid;
        }
        if(l==r) ans=l;
    }
    cout<<"! "<<ans<<endl;
    cout.flush();
    system("pause");
    return 0;
}