校长的密码

前言

当拿到这道题只想打暴力的时候,我就已经输了。

题目

题目大意

校长 ( t DD(XYX)) 在知乎上开启了隐私保护!为了得知校长的最新动态并跟随他的脚步,他忠诚的追随者们需要破解他的隐私密码。

校长的隐私密码由长达 (N) 位的数码组成,每一位的数码范围为 ([1,K])

校长的密码并非完全无迹可寻,根据校长的日常用语以及行为准则,他的追随者们得到了 (M) 个信息,每个信息有两个整数 (a_i,b_i) ,表示数码 (a_i) 不会出现在 (b_i) 之前。

为了估算破解密码的时间花费,你需要计算出一共有多少种不同的密码。

同时它的追随者们对每一种密码视为十进制数的算术和很感兴趣,认为这是校长神通广大的原力,所以你也需要计算出来,而且不允许有任何作假!(不取模)

(1le Nle 500;0le Mle 100;Kle 9.)

嘿,小声点,小心被校长发现了!还有,动作快点,(1s) 是极限了!

样例

样例输入1

4 4 3
1 1
1 2
2 2
3 1

样例输出1

7
19020

讲解

这道题 (M=0) 的数据有 (50pts),加上 (Nle 6)(20pts) 就有 (70pts),所以完全没打算想正解,佛了。

考试结束后一琢磨,这不是状压+高精板题吗?!

从高位向低位DP可以避免高精乘高精。

时间复杂度 (O(2^KKNC)),其中 (C) 是高精度复杂度。

代码

由于OJ太烂而需要大力卡常还加了Ofast的代码
//12252024832524
#pragma GCC optimize("Ofast")
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;
  
typedef long long LL;
const int MAXN = 100005;
int n,m,k;
  
LL Read()
{
    LL x = 0,f = 1; char c = getchar();
    while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
    while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    return x * f;
}
TT void Put1(T x)
{
    if(x > 9) Put1(x/10);
    putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
    if(x < 0) putchar('-'),x = -x;
    Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Min(T x){return x < 0 ? -x : x;}
  
const LL N = 100000000000000000ll;
struct BigNum
{
    bool hv;
    int len;
    LL a[145];
      
    BigNum(){memset(a,0,sizeof(a));}
      
    void init(LL x)
    {
        for(int i = 0;i <= len;++ i) a[i] = 0;
        len = -1; hv = 1;
        do
        {
            a[++len] = x%N;
            x/=N;
        }while(x);
    }
      
    BigNum operator + (const BigNum &C)const
    {
        BigNum ret; ret.len = Max(len,C.len); ret.hv = 1;
        LL jw = 0;
        for(int i = 0;i <= ret.len;++ i)
        {
            ret.a[i] = a[i]+C.a[i]+jw;
            jw = ret.a[i] / N; 
            ret.a[i] %= N;
        }
        while(jw) ret.a[++ret.len] = jw%N,jw/=N;
        return ret;
    }
      
    BigNum operator * (const LL x)const
    {
        BigNum ret; LL jw = 0; ret.len = len; ret.hv = 1;
        for(int i = 0;i <= len;++ i)
        {
            ret.a[i] = a[i]*x+jw;
            jw = ret.a[i] / N;
            ret.a[i] %= N;
        }
        while(jw) ret.a[++ret.len] = jw%N,jw/=N;
        return ret;
    }
     
    void operator += (const BigNum &C)
    {
        LL jw = 0; len = Max(len,C.len); hv = 1;
        for(int i = 0;i <= len;++ i)
        {
            if(!jw && i > C.len) break;
            a[i] = a[i]+C.a[i]+jw;
            jw = a[i]/N;
            a[i] %= N;
        }
        while(jw) a[++len] = jw%N,jw/=N;
    }
      
    void print()
    {
        if(len < 0) return; Put(a[len]);
        for(int i = len-1;i >= 0;-- i) printf("%017lld",a[i]);
        putchar('
');
    }
};
int fk[10][10];
BigNum cnt[2][1<<9],dp[2][1<<9];
bool can[9][1<<9];
int lowbit(int x){return x & -x;}
  
int main()
{
    freopen("hacker2.in","r",stdin);
    freopen("hacker2.out","w",stdout);
    n = Read(); m = Read(); k = Read(); 
    for(int i = 1;i <= m;++ i)
    {
        int u = Read(),v = Read();
        fk[u-1][v-1] = 1;
    }
    int sx = (1<<k) - 1;
    for(int S = 0;S <= sx;++ S)
        for(int i = 0;i < k;++ i)
        {
            can[i][S] = 1;
            for(int j = 0;j < k;++ j)
                if((S >> j & 1) && fk[j][i])
                    can[i][S] = 0;
        }
    dp[0][0].init(0); cnt[0][0].init(1);
    for(int i = 1;i <= n;++ i)
    {
        bool now = i&1;
        for(int S = 0;S <= sx;++ S) dp[now][S].init(0),cnt[now][S].init(0),dp[now][S].hv = cnt[now][S].hv = 0;
        for(int S = 0;S <= sx;++ S)
        {
        	dp[now^1][S]=dp[now^1][S]*10;
			for(int j = 0;j < k;++ j)
            {
                if(!can[j][S] || !cnt[now^1][S].hv) continue;
                dp[now][S|(1<<j)] += dp[now^1][S];
				dp[now][S|(1<<j)] += cnt[now^1][S]*(j+1);
                cnt[now][S|(1<<j)] += cnt[now^1][S];
            }
        }
    }
    BigNum ans1,ans2; ans1.init(0); ans2.init(0);
    for(int S = 0;S <= sx;++ S) ans1 += cnt[n&1][S],ans2 += dp[n&1][S];
    ans1.print(); ans2.print(); 
    return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/15371996.html