方伯伯的商场之旅

本来想找道水题,结果,,,

我又被坑了,,

捣鼓了半天出的思路,打了一个晚自习,A了后一找题解,发现大家想的好像都和我不一样,,

翻了一会儿,猛然发现还是有dalao跟我想的一样Orz,写得比身为魔芋的我好多了,,于是,就(Ctrl  C+Ctrl  V)借鉴了一波

但是,,我还是把我的丑鬼代码存个档吧

对于每个数,它一定会在集合点为某一位,然后该集合点数为0~k-1的某值时被找到(把所有石子移到集合点)

那么,我们可以枚举集合点(mid)与集合点的数(midv),在套上数位dp,就可以得到答案

怎么套?

如果能转移,那么我们的代价必须减少。

mid左移一位,改变量为S2+midv-S1;

mid右移一位,改变量为S1+midv-S2;

那么,得到某个数时,我们的要求就是改变量>=0,即改变量>=-midv且改变量<=midv,满足的话这个数最优解就是这种情况,反之则不是

去重变成左开右闭

找每一位,变化量,是否被限制,我们就可以得到花费

记忆化爆搜即可

在下自以为注释写得还行,是在没懂可以看看(非主流做法,被带歪了别怪我)

//注:方案数实为在这种方案,即mid与midv为枚举出的定值时最优的数的个数
#include<bits/stdc++.h>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<deque>
#include<list>
#include<set>
#include<vector>
#include<iostream>
#define ll long long
#define re register
#define inf 0x3f3f3f3f
#define inl inline
#define sqr(x) (x*x)
//#define eps 1e-8
#define debug printf("debug\n");
//#pragma comment(linker, "/STACK:1024000000,1024000000")
//#pragma GCC optimize (2)
//#pragma G++ optimize (2)
using namespace std;
//const ll mod;
const ll MAXN=3e5+10;
inl ll read() {
    re ll x = 0; re int f = 1;
    char ch = getchar();
    while(ch<'0'||ch>'9') { if(ch== '-' ) f = -1; ch = getchar(); }
    while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x * f;
}
inl char readc() {
    char ch=getchar();
    while(('z'<ch||ch<'a')&&('Z'<ch||ch<'A')) ch=getchar();
    return ch;
}
inl void write(re ll x){
    if(x>=10)write(x/10);
    putchar(x%10+'0');
}
inl void writeln(re ll x){
    if(x<0) {x=-x;putchar('-');}
    write(x); puts("");
}
inl ll gcd(re ll x,re ll y){while(y^=x^=y^=x%=y);return x;}
inl void FR() {
    freopen(".in","r",stdin);
    freopen(".out","w",stdout);
}
inl void FC() {
    fclose(stdin);
    fclose(stdout);
}
ll l,r,k,cnt,mid,midv,a[50];//集合点是第几位,集合点那个数是多少 
ll f[60][505][2],g[60][505][2];//f表示费用 ,g表示方案数 
ll dp(ll x,ll y,ll t) {
    if(x==cnt+1) return 0;
    return f[x][y][t];
}//找花费(已在solve中求解出) 
ll solve(ll x,ll y,bool limit) {//第几位,S2-S1,是否被限制 
    if(x==cnt+1) {
        re ll ssy=y-250;
        if(ssy>-midv&&ssy<=midv) return 1;//改变量为:S2(mid后面的和)-S1(mid前面的和)+midv(集合点的数的个数) 
        else return 0;
    }//最后一位,如果可行返回1,反之则0 
    if(~f[x][y][limit]) return g[x][y][limit];//求过方案,直接返回 
    re ll ans=0,num=0;
    if(x==mid) {
        if(!limit || midv<=a[x]) {
            ans+=solve(x+1,y,limit&(midv==a[x]));
            num+=dp(x+1,y,limit&(midv==a[x]));
        }//+限制,不可乱跑方案与花费+不限制,可乱跑方案与花费 
    }//刚好第mid位 
    else if (limit) {
        for (re ll i=0;i<=a[x];i++) {
            re ll sum=i*((x>mid)?-1:1);//左边或右边多了一些数 
            re ll t=solve(x+1,y+sum,i==a[x]);//往后找方案数 
            ans+=t;
            num+=abs(x-mid)*i*t+dp(x+1,y+sum,i==a[x]);//花费加上这一段方案数(这么选是最优解的数的个数)*这一位是多少*这一段位移长度+后面的花费
            //因为如果这一位可行,那么这一位-1得到的数一定也是以mid,midv取该值为最优,所以*i 
        }
    }//被限制 
    else if (!limit) {
        for (re ll i=0;i<k;i++) {
            re ll sum=i*((x>mid)?-1:1);
            re ll t=solve(x+1,y+sum,0);
            ans+=t;
            num+=abs(x-mid)*i*t+dp(x+1,y+sum,0);
        }
    }//无限制,与上面类似 
    f[x][y][limit]=num;
    return g[x][y][limit]=ans;//记忆化 
}
ll calc(ll x) {
    re ll ans=0;cnt=0;
    while(x) {a[++cnt]=x%k;x/=k;}
    for(re ll i=1;i<=cnt/2;i++) swap(a[i],a[cnt-i+1]);
    for(mid=1;mid<=cnt;mid++) {
        for(midv=1;midv<k;midv++) {
            memset(g,0,sizeof(g));
            memset(f,-1,sizeof(f));
            solve(1,250,1);
            ans+=f[1][250][1];//后面最多13个(L,R<=1e15,L或R等于1e15时后面14个全是0),13*19=247,卡一下 (祭奠一下在下这个开500的蒟蒻)
        }
    }
    return ans;
}
int main(){
//    FR();
    l=read(),r=read(),k=read();
    writeln(calc(r)-calc(l-1));
//    FC();
    return 0;
}

 

转载于:https://www.cnblogs.com/20020723YJX/p/9374661.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个相对复杂的问题,需要使用树的相关知识和Java编程技术实现。下面是一个简单的实现示例,供您参考: ``` import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Scanner; public class FamilyTree { // 家族成员类 static class Member { String name; // 姓名 Member father; // 父亲 List<Member> children; // 孩子 public Member(String name) { this.name = name; this.children = new ArrayList<>(); } // 添加孩子 public void addChild(Member child) { children.add(child); child.father = this; } } public static void main(String[] args) { // 初始族谱 Member root = new Member("祖先"); Member m1 = new Member("父亲"); Member m2 = new Member("叔叔"); Member m3 = new Member("伯伯"); Member m4 = new Member("儿子1"); Member m5 = new Member("儿子2"); Member m6 = new Member("侄子1"); Member m7 = new Member("侄子2"); Member m8 = new Member("侄孙1"); Member m9 = new Member("侄孙2"); Member m10 = new Member("孙子1"); Member m11 = new Member("孙子2"); root.addChild(m1); root.addChild(m2); root.addChild(m3); m1.addChild(m4); m1.addChild(m5); m2.addChild(m6); m2.addChild(m7); m6.addChild(m8); m7.addChild(m9); m4.addChild(m10); m5.addChild(m11); // 构建家族成员Map,便查询 Map<String, Member> members = new HashMap<>(); members.put(root.name, root); members.put(m1.name, m1); members.put(m2.name, m2); members.put(m3.name, m3); members.put(m4.name, m4); members.put(m5.name, m5); members.put(m6.name, m6); members.put(m7.name, m7); members.put(m8.name, m8); members.put(m9.name, m9); members.put(m10.name, m10); members.put(m11.name, m11); // 测试查询和修改功能 Scanner scanner = new Scanner(System.in); while (true) { System.out.println("请输入要查询或修改的家族成员姓名:"); String name = scanner.nextLine(); Member member = members.get(name); if (member == null) { System.out.println("找不到该成员!"); continue; } System.out.println("该成员的父亲是:" + (member.father == null ? "无" : member.father.name)); System.out.println("该成员的孩子有:"); for (Member child : member.children) { System.out.println(child.name); } System.out.println("请选择要进行的操作:1-新增孩子,2-退出"); int option = scanner.nextInt(); scanner.nextLine(); if (option == 1) { System.out.println("请输入要新增孩子的姓名:"); String childName = scanner.nextLine(); Member child = new Member(childName); member.addChild(child); members.put(childName, child); System.out.println("新增孩子成功!"); } else if (option == 2) { break; } else { System.out.println("无效的操作!"); } } } } ``` 在这个示例中,我们使用了树的数据结构来存储家族成员的族谱,每个成员都是一个节点,有父亲和孩子。构建家族成员Map可以便地查询和修改家族成员。在主函数中,我们先构建了一个初始的家族成员,然后通过Scanner从键盘输入要查询或修改的成员姓名,查询该成员的父亲和孩子,并提供了新增孩子的功能,直到用户选择退出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值