大二上第十三周学习笔记

周三

F - The Answer to the Ultimate Question of Life, The Universe, and Everything. (打表)

求a ^ 3 + b ^ 3 + c ^ 3 = x 有无解 有则输出

a b c 的绝对值小于5000 x在0到200

我看到x范围比较小就觉得可以打表

问题是怎么写一个比较快的打表程序

一开始想的是c^3用map存,然后对于每一个x枚举a b

但这样太慢了

其实很接近了

应该是a ^ 3 + b ^ 3用map存,然后每次枚举c

这样打表的就很快了

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(ll i = (a); i <= (b); i++)
using namespace std;

int ans[250][3] = 
{
0,5000,-5000,
1,5000,-5000,
-486,4375,-4373,
4,4,-5,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-205,644,-637,
44,168,-169,
2,5000,-5000,
-52,217,-216,
-353,683,-650,
-641,843,-695,
7,10,-11,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-262,332,-265,
-588,4118,-4114,
2195,2977,-3331,
-1276,1671,-1373,
47,91,-95,
-741,2833,-2816,
-287,445,-401,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
8,8,-10,
1839,2357,-2683,
237,2106,-2107,
3,5000,-5000,
-249,2269,-2268,
-69,235,-233,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-244,1557,-1555,
-509,1154,-1120,
2358,2731,-3223,
-84,445,-444,
16,25,-27,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-307,837,-823,
-5,8,-7,
1709,2025,-2369,
-473,815,-758,
49,139,-141,
-1247,3991,-3950,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
602,659,-796,
1000000000,1000000000,1000000000,
1518,2141,-2370,
-648,3891,-3885,
1837,3131,-3329,
505,559,-672,
361,982,-998,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-163,1202,-1201,
668,845,-966,
-1561,2903,-2744,
102,146,-161,
4,5000,-5000,
403,903,-929,
1,4,1,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
134,398,-403,
824,2325,-2359,
401,443,-533,
-104,434,-432,
-146,344,-335,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-829,2123,-2080,
-196,711,-706,
-706,1366,-1300,
-1719,2638,-2368,
847,1188,-1317,
1315,3651,-3707,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-1972,4271,-4126,
-1282,1686,-1390,
1953,2036,-2514,
365,1798,-1803,
-2912,3992,-3389,
861,4039,-4052,
-98,253,-248,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
14,20,-22,
-991,3200,-3168,
-1638,2391,-2101,
-622,984,-893,
-903,1870,-1797,
319,2325,-2327,
118,229,-239,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-4,8,-7,
-1165,2760,-2689,
947,1117,-1309,
-948,1345,-1165,
853,2924,-2948,
1000000000,1000000000,1000000000,
-2312,4966,-4793,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
8,11,-12,
-757,1945,-1906,
-555,962,-896,
383,4327,-4328,
-1673,3789,-3677,
1219,2725,-2804,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-16,38,-37,
0,5,-1,
5,5000,-5000,
-419,2217,-2212,
-3881,4988,-4034,
-726,3997,-3989,
-1238,1801,-1580,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
2,5,-1,
167,389,-399,
-1766,3203,-3013,
-629,1395,-1351,
816,946,-1116,
-428,801,-758,
-77,103,-86,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
104,116,-139,
-3,8,-7,
1000000000,1000000000,1000000000,
-2552,3342,-2746,
-7,10,-8,
-263,376,-327,
1528,2131,-2366,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
260,317,-367,
215,447,-463,
486,741,-805,
-695,3744,-3736,
-516,2145,-2135,
-1049,3721,-3693,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
383,1526,-1534,
-1654,3972,-3874,
-2476,4980,-4767,
-1417,4180,-4125,
-2943,4033,-3423,
-59,79,-66,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-574,890,-802,
-1012,1521,-1354,
-2149,4047,-3834,
891,1178,-1328,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-170,349,-335,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-160,1169,-1168,
-10,15,-13,
1503,2691,-2839,
1000000000,1000000000,1000000000,
974,4861,-4874,
-29,91,-90,
976,4876,-4889,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
5,5,-4,
-1092,2000,-1885,
318,1635,-1639,
-1403,1974,-1702,
-593,4815,-4812,
-215,399,-377,
16,16,-20,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
1000000000,1000000000,1000000000,
-579,1112,-1057,
-1606,3026,-2867,
-1347,3809,-3752,
508,2199,-2208,
-638,2334,-2318,
};


int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int x; scanf("%d", &x);
        if(ans[x][0] == 1e9) puts("impossible");
        else printf("%d %d %d\n", ans[x][0], ans[x][1], ans[x][2]);
    }

    return 0;
}

C - <3 numbers 

很容易想到质数的分布是比较稀疏的

所以大范围是肯定小于1 / 3的

小范围暴力

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

int check(int x)
{
    if(x == 1) return true;
    for(int i = 2; i * i <= x; i++)
        if(x % i == 0)
            return false;
    return true;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        if(r - l > 1e3) puts("Yes");
        else
        {
            int cnt = 0;
            _for(i, l, r)
                cnt += check(i);
            if(3 * cnt < (r - l + 1)) puts("Yes");
            else puts("No");
        }
    }

    return 0;
}

E - Multiply (大数质数判定与分解质因数)

 套了kuangbin的模板

但一直WA

其实思路完全正确,就是小细节没弄好

一个是计算因数出现了多少次的时候确实会爆long long 因为是阶乘的因子数不是一个数的因子数

一个是有个地方会爆long long 我意识到了但没处理好

应该反过来写,用除法 就可以避免这个问题

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

typedef long long ll;
const int N = 1e5 + 10;
const int S = 8;
ll x, y, a[N];
int m;

ll factor[100];
int tol;

ll gcd(ll a, ll b) 
{ 
    ll t;
    while(b)
    {
        t = a;
        a = b;
        b = t % b;
    }
    if(a >= 0) return a;
    return -a;
}

ll binpow(ll a, ll b, ll c)
{
    a %= c;
    b %= c;
    ll ret = 0, tmp = a;
    while(b)
    {
        if(b & 1)
        {
            ret += tmp;
            if(ret > c) ret -= c;
        }
        tmp <<= 1;
        if(tmp > c) tmp -= c;
        b >>= 1;
    }
    return ret;
}

ll pow_mod(ll a, ll n, ll mod)
{
    ll ret = 1;
    ll temp = a % mod;
    while(n)
    {
        if(n & 1) ret = binpow(ret, temp, mod);
        temp = binpow(temp, temp, mod);
        n >>= 1;
    }
    return ret;
}

bool check(ll a, ll n, ll x, ll t)
{
    ll ret = pow_mod(a, x, n);
    ll last = ret;
    _for(i, 1, t)
    {
        ret = binpow(ret, ret, n);
        if(ret == 1 && last != 1 && last != n - 1) return true;
        last = ret;
    }
    if(ret != 1) return true;
    return false;
}

bool Miller_Rabin(ll n)
{
    if(n < 2) return false;
    if(n == 2) return true;
    if((n & 1) == 0) return false;
    ll x = n - 1, t = 0;
    while((x & 1) == 0) x >>= 1, t++;

    srand(time(0));

    for(int i = 0; i < S; i++)
    {
        ll a = rand() % (n - 1) + 1;
        if(check(a, n, x, t)) return false;
    }
    return true;
}

ll rho(ll x, ll c)
{
    ll i = 1, k = 2;
    srand(time(0));
    ll x0 = rand() % (x - 1) + 1;
    ll y = x0;
    while(1)
    {
        i++;
        x0 = (binpow(x0, x0, x) + c) % x;
        ll d = gcd(y - x0, x);
        if(d != 1 && d != x) return d;
        if(y == x0) return x;
        if(i == k) { y = x0; k += k; }
    }
}

void findfac(ll n, int k)
{
    if(n == 1) return;
    if(Miller_Rabin(n))
    {
        factor[tol++] = n;
        return;
    }
    ll p = n;
    int c = k;
    while(p >= n) p = rho(p, c--);
    findfac(p, k);
    findfac(n / p, k);
}

vector> p;
map mp;
ll cnta[100], cnty[100];

void add(ll cur, ll cnt[])
{
    rep(i, 0, p.size())
    {
        ll now = p[i].first, t = cur;
        while(t)
        {
            cnt[i] += t / now;
            t /= now;
        }
    }
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        tol = 0;
        scanf("%d%lld%lld", &m, &x, &y);
        _for(i, 1, m) scanf("%lld", &a[i]);
        findfac(x, 107);

        mp.clear(); p.clear();
        rep(i, 0, tol) mp[factor[i]]++;
        for(auto t: mp) p.push_back({t.first, t.second});

        memset(cnta, 0, sizeof cnta);
        memset(cnty, 0, sizeof cnty);
        add(y, cnty);
        _for(i, 1, m) add(a[i], cnta);

        ll ans = 1e18;
        rep(i, 0, p.size()) ans = min(ans, (cnty[i] - cnta[i]) / p[i].second);
        printf("%lld\n", ans);
    }

    return 0;
}

M - Kill the tree (树的重心)

这道题把树的重心考到极致

先复习一下

1.树的重心指的是以重心为根的树,所有子树的节点数的最大值最小。

要求重心可以处理出子树节点数,算出以每个节点为根的树的子树节点最大值,然后比较就行

2.树的重心到所有节点的距离和最小

3.树的重心最多也两个,若有两个一定相邻

4.两个树合并时,新的重心一定在两个重心连的链上

这道题问题其实就是合并重心,在dfs的过程中不断合并

首先重心在两个重心的链上

一个重心移动时,可以计算一下这个移动所影响的距离和的值,根据子树节点可得

这个值变小就可以移动,一直移动到极值

可以发现其中一个节点会移动到根,一个节点不会。没到根的那个就是重心

可以根据深度来区分

最后注意一下两个重心的情况就好了

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 2e5 + 10;
vector g[N];
int f[N], d[N], siz[N], ans[N], n;

void init(int u, int fa)
{
    f[u] = fa;
    d[u] = d[fa] + 1;
    for(int v: g[u])
    {
        if(v == fa) continue;
        init(v, u);
    }
}

void dfs(int u, int fa)
{
    siz[u] = 1;
    ans[u] = u;
    for(int v: g[u])
    {
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
        int a = ans[u], b = ans[v];
        while(siz[a] < siz[u] - siz[a]) a = f[a];
        while(siz[b] < siz[u] - siz[b]) b = f[b];
        ans[u] = d[a] > d[b] ? a: b;
    }
}

int main()
{
    scanf("%d", &n);
    _for(i, 1, n - 1)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back(v);
        g[v].push_back(u);
    }

    init(1, 0);
    dfs(1, 0);

    _for(i, 1, n)
    {
        int u = ans[i];
        if(siz[u] != siz[i] - siz[u]) printf("%d\n", u);
        else
        {
            int v = f[u];
            if(u > v) swap(u, v);
            printf("%d %d\n", u, v);
        } 
    }

    return 0;
}

D. Bag of mice(概率dp)

用dp解决概率问题

分类讨论就行

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e3 + 10;
double dp[N][N];
int w, b;

double cal(int a, int b) { return (double)a / b; }

int main()
{
    scanf("%d%d", &w, &b);
    _for(j, 0, b) dp[0][j] = 0;
    _for(i, 1, w) dp[i][0] = 1;

    _for(i, 1, w)
        _for(j, 1, b)
        {
            dp[i][j] += cal(i, i + j);
            if(j >= 3) dp[i][j] += cal(j, i + j) * cal(j - 1, i + j - 1) * cal(j - 2, i + j - 2) * dp[i][j - 3];
            if(j >= 2 && i >= 1) dp[i][j] += cal(j, i + j) * cal(j - 1, i + j - 1) * cal(i, i + j - 2) * dp[i - 1][j - 2];
        }
    printf("%.12f\n", dp[w][b]);

    return 0;
}

周四

Collecting Bugs(期望dp)

期望dp推公式就可以了

注意几个地方

1.概率dp设计状态是顺推,期望dp是逆推

2.期望dp推公式的时候可能等式右边会出现自己,移到左边即可

3.注意期望的含义

一般是dpij = Σpi (当前步的花费 + dp……)

可以理解为dpij = Σpi * dp…… + 当前步的花费

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e3 + 10;
double dp[N][N];
int n, s;

int main()
{
    scanf("%d%d", &n, &s);
    for(int i = n; i >= 0; i--)
        for(int j = s; j >= 0; j--)
        {
            if(i == n && j == s) continue;
            double k1 = (double)i / n, k2 = (double)j / s;
            dp[i][j] = (1 + k1 * (1 - k2) * dp[i][j + 1] + (1 - k1) * k2 * dp[i + 1][j] 
                + (1 - k1) * (1 - k2) * dp[i + 1][j + 1]) / (1 - k1 * k2);
        }
    printf("%.6f\n", dp[0][0]);
    return 0;
}

HDU 3853  (期望dp)

和上一题类似

期望dp逆推就行

注意可能停在当前地方的概率为百分百,要特判一下

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e3 + 10;
double dp[N][N], a[N][N], b[N][N], c[N][N];
int n, m;

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        _for(i, 1, n)
            _for(j, 1, m)
                scanf("%lf%lf%lf", &a[i][j], &b[i][j], &c[i][j]);
        memset(dp, 0, sizeof dp);
        for(int i = n; i >= 1; i--)
            for(int j = m; j >= 1; j--)
            {
                if(i == n && j == m) continue;
                if(a[i][j] == 1) continue;
                dp[i][j] = (b[i][j] * dp[i][j + 1] + c[i][j] * dp[i + 1][j] + 2) / (1 - a[i][j]);
            }
        printf("%.3f\n", dp[1][1]);
    }

    return 0;
}

周五

F - Honeycomb

把输入处理一下bfs即可

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

const int N = 1e3 + 10;
const int M = 1e6 + 10;
char s[4 * N][6 * N];
int d[M], n, m, st, t;
vector g[M];

int id(int i, int j) { return (i - 1) * m + j; }

int bfs()
{
    _for(i, 1, n)
        _for(j, 1, m)
            d[id(i, j)] = 0;
    queue q;
    q.push(st);
    d[st] = 1;

    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int v: g[u])
        {
            if(d[v]) continue;
            d[v] = d[u] + 1;
            if(v == t) return d[v];
            q.push(v);
        }
    }
    return -1;
}

int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &m);
        getchar();
        _for(i, 1, 4 * n + 3) gets(s[i] + 1);
        _for(i, 1, n * m) g[i].clear();

        _for(i, 1, n)
            _for(j, 1, m)
            {
                int x, y;
                if(j % 2 == 1) x = 4 * i - 1;
                else x = 4 * i + 1;
                y = 6 * j - 1;

                if(s[x][y] == 'S') st = id(i, j);
                if(s[x][y] == 'T') t = id(i, j);

                if(j % 2 == 0)
                {
                    if(s[x + 1][y + 3] == ' ') g[id(i, j)].push_back(id(i + 1, j + 1));
                    if(s[x + 1][y - 3] == ' ') g[id(i, j)].push_back(id(i + 1, j - 1));
                    if(s[x - 1][y + 3] == ' ') g[id(i, j)].push_back(id(i, j + 1));
                    if(s[x - 1][y - 3] == ' ') g[id(i, j)].push_back(id(i, j - 1));
                    if(s[x + 2][y] == ' ') g[id(i, j)].push_back(id(i + 1, j));
                    if(s[x - 2][y] == ' ') g[id(i, j)].push_back(id(i - 1, j));
                }
                else
                {
                    if(s[x + 1][y + 3] == ' ') g[id(i, j)].push_back(id(i, j + 1));
                    if(s[x + 1][y - 3] == ' ') g[id(i, j)].push_back(id(i, j - 1));
                    if(s[x - 1][y + 3] == ' ') g[id(i, j)].push_back(id(i - 1, j + 1));
                    if(s[x - 1][y - 3] == ' ') g[id(i, j)].push_back(id(i - 1, j - 1));
                    if(s[x + 2][y] == ' ') g[id(i, j)].push_back(id(i + 1, j));
                    if(s[x - 2][y] == ' ') g[id(i, j)].push_back(id(i - 1, j));
                }
            }
        printf("%d\n", bfs());
    }

    return 0;
}

E - Resistors in Parallel (数学 + 高精度)

打表可以看出来 

但比赛时还是推出来的

首先题目说了没有平方因子

那对于一个数,肯定是它的所有因子选0此方或者1此方

那么肯定是前几个质数

代入公式可得答案为 n除以一个求和

n是当前要选的数

求和是因子之和

发现这个值可以递推

每次多一个质数就是乘以质数加1,因为是在前面的结果之下这个值选或者不选

那么就还剩下一个怎么约分的问题

每次分子乘一个p,分母乘p + 1

分子要和分母约分,要满足pi = pj + 1
也就是相邻的质数差1,可以发现只有2 和 3 这个可以特判一下

p + 1要和分子约分,这个可以暴力枚举p + 1的因子,看是不是分母的因子,是的话就约分

要用高精度

高精度乘以低精,除以低精度,模低精度

#include
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;

struct bignum
{
    int s[10000], len;
    bignum() { memset(s, 0, sizeof s); len = 0; }

    void print()
    {
        for(int i = len; i >= 1; i--) printf("%d", s[i]);
    }
};

bignum mul(bignum a, int t)
{
    _for(i, 1, a.len) a.s[i] *= t;
    _for(i, 1, a.len)
    {
        a.s[i + 1] += a.s[i] / 10;
        a.s[i] %= 10;
    }
    while(a.s[a.len + 1])
    {
        a.len++;
        a.s[a.len + 1] += a.s[a.len] / 10;
        a.s[a.len] %= 10;
    }
    return a;
}

bignum div(bignum a, int t)
{
    int& len = a.len;
    for(int i = len; i >= 1; i--)
    {
        if(i - 1 >= 1) a.s[i - 1] += (a.s[i] % t) * 10;
        a.s[i] /= t;
    }
    while(len && !a.s[len]) len--;
    return a;
}

int mol(bignum a, int t)
{
    int d = 0;
    for(int i = a.len; i >= 1; i--)
        d = (d * 10 + a.s[i]) % t;
    return d;
}

bool check(int x)
{
    if(x == 1 || x == 0) return false;
    for(int i = 2; i * i <= x; i++)
        if(x % i == 0)
            return false;
    return true;
}
vector p;

int main()
{
    _for(i, 2, 300)
        if(check(i))
            p.push_back(i);

    int T; scanf("%d", &T);
    while(T--)
    {
        string s;
        cin >> s;
        int len = s.size();

        if(s == "1")
        {
            puts("1/1");
            continue;
        }

        bignum x; int t = 0;
        x.len = len;
        for(int i = len - 1; i >= 0; i--)
            x.s[++t] = s[i] - '0';

        int pos;
        rep(i, 0, p.size())
        {
            x = div(x, p[i]);
            if(!x.len) break;
            pos = i;
        }
        
        vector ans;
        _for(i, 0, pos) ans.push_back(p[i]);

        if(ans.size() == 1) 
        {
            puts("2/3");
            continue;
        }
        else if(ans.size() == 2)
        {
            puts("1/2");
            continue;
        }

        bignum up;  up.len = 1; up.s[1] = 1;
        bignum down;  down.len = 1; down.s[1] = 2;

        rep(i, 2, ans.size())
        {
            int now = p[i] + 1;
            for(int j = now - 1; j >= 1; j--)
                if(now % j == 0 && mol(up, j) == 0)
                {
                    now /= j;
                    up = div(up, j);
                    break;
                }
            up = mul(up, p[i]);
            down = mul(down, now);
        }
        up.print();
        putchar('/');
        down.print();
        puts("");
    }

    return 0;
}

网络流建模技巧

总结了一下 写在这篇博客

网络流建模技巧_Keep going, don't settle.-CSDN博客

CF16E Fish (概率+状压dp)

n只有18那果断状压dp

注意每次转移的时候,两条鱼相遇还要乘上一个概率,即1 / C(m, 2) m为当前存活的鱼的数目

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
double dp[1 << 18], a[20][20];
int n;

int main()
{
    scanf("%d", &n);
    rep(i, 0, n)
        rep(j, 0, n)
            scanf("%lf", &a[i][j]);
    
    dp[0] = 1;
    rep(S, 0, 1 << n)
    {
        rep(i, 0, n)
            if(S & (1 << i))
            {
                int cnt = 1;
                double sum = 0;
                rep(j, 0, n)
                    if(j != i && !(S & (1 << j)))
                        sum += a[j][i], cnt++;
                if(cnt) dp[S] += dp[S ^ (1 << i)] * 2 / cnt / (cnt - 1) * sum;
            }
    }

    int all = (1 << n) - 1;
    rep(i, 0, n) printf("%.6f ", dp[all ^ (1 << i)]);

	return 0;
}

CF621E Wet Shark and Blocks(矩阵快速幂优化动态规划)

看到b有1e9一下子懵逼了

但是考虑的时候我先不考虑1e9,先想dp

dp[i][j]表示前i位余数为j的方案数

一开始我想的是从低位开始,发现不好弄

然后我就从高位开始,就好处理很多,每次余数乘以10加上当前的数即可

可以发现每次转移都是类似的,固定的i都转移到固定的一些j

那问题来了,dp方程都写好了,怎么处理1e9

很快想到矩阵快速幂,但是之前都是优化递推式,手算矩阵的,这个是不定的,我这道题就卡在这里

看了题解后发现,构造一个矩阵A,如果余数i转化为j,那就Aij++

这样子,乘上这个矩阵就恰好完成了一次转移

我验证了一下,恰好是这样

dp的初始状态是dp[0][0] = 1

也就是这样一个矩阵[1, 0, 0, 0……]

手算可得,dp[b][k]显然就是A^b矩阵的[0][k]这个位置

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 150;
struct node
{
    ll s[N][N];
    node() { memset(s, 0, sizeof s); }
}A;
int n, b, k, x;

node mul(node a, node b)
{
    node c;
    rep(i, 0, x)
        rep(j, 0, x)
            rep(t, 0, x)
                c.s[i][j] = (c.s[i][j] + a.s[i][t] * b.s[t][j]) % mod;
    return c;
}

int main()
{
    scanf("%d%d%d%d", &n, &b, &k, &x);
    _for(i, 1, n)
    {
        int cur; scanf("%d", &cur);
        rep(j, 0, x)
            A.s[j][(j * 10 + cur) % x]++;
    }

    node res;
    rep(i, 0, x) res.s[i][i] = 1;
    for(; b; b >>= 1)
    {
        if(b & 1) res = mul(res, A);
        A = mul(A, A);
    }
    printf("%lld\n", res.s[0][k]);

	return 0;
}

E. Number With The Given Amount Of Divisors(因数个数 + 搜索)

独立做出

一个数为p1 ^ k1 * p2 ^ k2……

因数个数为(k1 + 1) (k2 + 1) ……

因此可以暴力将n分解成很多个因子

要使数最小,显然选前几个质数

把这些因子分配到前几个因子就可以了

注意longlong如果爆了就变成负数了

#include 
#define rep(i, a, b) for(int i = (a); i < (b); i++)
#define _for(i, a, b) for(int i = (a); i <= (b); i++)
using namespace std;
 
typedef long long LL;
LL ans = 1e18;

int p[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43};

LL cal(LL a, int b)
{
    LL res =  1;
    _for(i, 1, b) 
    {
        res *= a;
        if(res < 0) return -1;
    }
    return res;
}

void dfs(int n, LL cur, int pos)
{
    if(cur < 0 || cur > ans) return;
    if(n == 1) 
    {
        ans = min(ans, cur);
        return;
    }
    
    for(int i = 2; i <= n; i++)
        if(n % i == 0)
            dfs(n / i, cur * cal(p[pos], i - 1), pos + 1);
}

int main()
{
    int n;
    scanf("%d", &n);
    dfs(n, 1, 0);
    printf("%lld\n", ans);
	return 0;
}

今天兴致很高刷了很多题还做了网络流总结

最近一个感悟是,做你该做的,然后对结果不报期望

太多报很大期望结果事与愿违了。还是不报期望吧。不报期望反而没什么压力,内心平静

你可能感兴趣的:(ACM学习笔记,算法)