CF855E,状压数位DP

记叙一下当时的比赛情况。
开场2分钟看懂A,3分钟写完交上去,CE,发现没有#include<cstdio。我好菜啊。
然后10分钟时过掉B的pt,开C
20分钟时来了个提醒:注意B题答案可能为负。
突然想起来B题里有一句话const ll int=1ll<<60。而 260<31018 ,gg。
改完后B只剩897分,于是满怀怨恨地锁了B,发现有个人跟我错的一样,叉了。
52分钟时写完C,wa4。
查了很久无果。
中途发现有一句无用的话if(!e[h[x]].next)return;
接着捡了第二个hack
然后造了棵两个点的树,发现自己挂了。因为此时根的度为1,不会dfs到2号点,改完就AC了。
好像过掉E的比过掉D的多,果断开E。
深夜了,意识很模糊。
这个E是数位DP,而且可能只支持 O(qlogr)
首先状压每一位的奇偶性是可行的,但这至少要 O(q2b) 显然会挂。
然后就放弃状压的思路,试图只存储出现奇数次的digit的次数。
最后15分钟弃疗,捡到了第3个hack。
赛后去看别人代码,发现状压复杂度是对的!因为总状态数只有 O(blogrb2b) ,每次不一定要从头算到尾,我好菜啊。
最后因为rating太低,还涨了3rating。
upd:注意dp数组初值不能赋成0,否则对于答案为零的状态起不到记忆化的效果,复杂度就不对了。

#include<cstdio>
#include<cstring>
#include<cctype>
typedef long long ll;
inline char read() {
    static const int IN_LEN = 1000000;
    static char buf[IN_LEN], *s, *t;
    if (s == t) {
        t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
        if (s == t) return -1;
    }
    return *s++;
}
template<class T>
inline void read(T &x) {
    static bool iosig;
    static char c;
    for (iosig = false, c = read(); !isdigit(c); c = read()) {
        if (c == '-') iosig = true;
        if (c == -1) return;
    }
    for (x = 0; isdigit(c); c = read())
        x = (x + (x << 2) << 1) + (c ^ '0');
    if (iosig) x = -x;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN], *oh = obuf;
inline void print(char c) {
    if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
    *oh++ = c;
}
template<class T>
inline void print(T x) {
    static int buf[30], cnt;
    if (x == 0) {
        print('0');
    } else {
        if (x < 0) print('-'), x = -x;
        for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
        while (cnt) print((char)buf[cnt--]);
    }
}
inline void flush() {
    fwrite(obuf, 1, oh - obuf, stdout);
}
const int N=5000;
int q,b,cnt[5000],w,a[70];
ll l,r,f[12][70][(1<<11)+5];
ll dfs(int x,int s,bool zero,bool limit){
    if(cnt[s]>x)return 0;
    if(!x)return 1;
    if(~f[b][x][s] && !zero && !limit)return f[b][x][s];
    ll ans=0;int i=1,up=limit?a[x]:b;
    if(limit && x>1)ans=dfs(x-1,s^(1<<a[x]),0,1);
    for(i=zero;i<up;++i)ans+=dfs(x-1,s^(1<<i),0,0);
    if(!zero && !limit)f[b][x][s]=ans;
    return ans;
}
inline ll calc(ll x){
    register ll t=x,ans;register int i;
    for(w=0;t;t/=b)a[++w]=t%b;ans=dfs(w,0,1,1);
    for(i=w-1;i;--i)
        ans+=dfs(i,0,1,0);
    return ans;
}
int main(){
    //freopen("1.txt","r",stdin);
    memset(f,-1,sizeof f);
    register int i=1;
    for(;i<N;++i)cnt[i]=cnt[i>>1]+(i&1);
    read(q);
    while(q--){
        read(b),read(l),read(r);
        print(calc(r+1)-calc(l)),print('\n');
    }
    flush();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值