2022NOIPA层联测21

B. 学数学

打表只发现了连续的$(2, 8)(8, 30)(30, 112)$似乎比较有规律的样子,通过他们算出来的$a=xy+1,b=x^2+y^2$恰好是$2^2$倍数,如果是“以3为起点的链表”就是$3^2$.但是不知道有什么用……

于是枚举这个像链表头一样的东西$i*i*i<=n$,剩下的另一个数循环一下,每次让$x$等于找到的$y$再继续找。估分$60$.

结果由于没有预处理前缀和$TLE 20$,好像不久之前某$T4$的$KMP$还有更多的题都是这么错的……

有更高级的规律:从上一对$x,y$得到下一对的方式就是$x=y',y=ky'-x'$,把所有答案的最小值放进数组里排完序二分答案。

有一个细节就是乘法会越界,可以开$long double$也可以把它写成除法。

code
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 1e7 + 2;

int T, cnt;
ll n, a[maxn];

inline ll read()
{
    ll x = 0, 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;
}

int main()
{
    freopen("math.in", "r", stdin);
    freopen("math.out", "w", stdout);

    n = 1e18; a[++cnt] = 1;
    for(ll i=2; i<=1000000; i++)
    {
        ll k = i * i, x = i;
        for(ll y=i*i*i; y<=n; )
        {
            a[++cnt] = y;
            if((n + x) / y < k) break; //写成乘法因为会越界
            ll nxty = k * y - x; 
            x = y; y = nxty;
        }
    }
    sort(a+1, a+1+cnt);
    T = read();
    while(T--)
    {
        n = read();
        printf("%d\n", upper_bound(a+1, a+1+cnt, n)-a-1);
    }

    return 0;
}

C. 早该砍砍了

由于我的$dp$稳定输出qpow(2, n-1),不会改了就直接交了个这个。。

关于正经的暴力做法,有效的覆盖最多有$n-1$次,最短的有效区间长度是$2$,每次最少“同化”一个,于是$dfs$并记录步数,每一步枚举$l$和$r$,$map$记录过程中的每一个状态。

code
// ubsan: undefined
// accoders
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3007;
const int mod = 1e9 + 7;

int n, h[maxn], f[maxn][maxn], ans;
map<int, bool> mp;

inline int read()
{
    int x = 0, 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;
}

inline int getsum()
{
    int ans = 0;
    for(int i=1; i<=n; i++) ans = (ans << 1) + (ans << 3) + h[i];
    return ans;
}

void dfs(int now)
{
    int sum = getsum();
    if(mp.find(sum) == mp.end())
    {
        mp[sum] = 1; ans++;
    }
    if(now == n-1) return;
    int d[10];
    for(int i=1; i<=n; i++) d[i] = h[i];
    for(int i=1; i<=n; i++)
    {
        for(int j=2; i+j-1<=n; j++)
        {
            int mi = h[i];
            for(int k=i+1; k<=i+j-1; k++) mi = min(mi, h[k]);
            for(int k=i; k<=i+j-1; k++) h[k] = mi;
            dfs(now + 1);
            for(int k=i; k<=i+j-1; k++) h[k] = d[k];
        }
    }
}

void solve()
{
    for(int i=1; i<=n; i++) f[n][i] = n - i + 1;
    for(int i=n-1; i>=1; i--)
    {
        for(int j=1; j<=i; j++) f[i][j] = (f[i][j] + f[i+1][j]) % mod;
        for(int j=i-1; j>=1; j--) f[i][j] = (f[i][j] + f[i][j+1]) % mod;
    }
    printf("%d\n", f[1][1]);
    exit(0);
}

int main()
{
    freopen("cut.in", "r", stdin);
    freopen("cut.out", "w", stdout);
    
    n = read(); bool sp = 1;
    for(int i=1; i<=n; i++)
    {
        h[i] = read();
        if(h[i] != i) sp = 0;
    }
    if(sp) solve();
    dfs(0);
    printf("%d\n", ans);

    return 0;
}

鹤了一下……

把$i$看成时间,$f[i][j]$表示轮到$i$贡献的时候前缀的方案数,每一个点能贡献方案的区间是它作为最小值的区间,并且覆盖不可以跳跃。

从$l$开始如果$l$被覆盖,覆盖前和覆盖后增加的恰好是$f[i-1][l-1]$,再考虑$l+1$,有$l+1$是否被覆盖的情况,如果$l+1$被覆盖了还可以继续讨论$l$,方案数的增量就是$f[i-1][l]+f[i-1][l-1]$,发现可以求前缀和。

code
//任尘世繁华,唯有守护你的一切,才是我此生唯一的使命。
//——次元战争·红龙
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 3007;
const int mod = 1e9 + 7;

int f[maxn][maxn], n, a[maxn];

inline int read()
{
    int x = 0, 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;
}

void add(int &x, int y) {x += y; x = x >= mod ? x - mod : x;}

int main()
{
    freopen("cut.in", "r", stdin);
    freopen("cut.out", "w", stdout);
    
    n = read(); for(int i=1; i<=n; i++) a[i] = read();
    f[0][0] = 1;
    for(int i=1; i<=n; i++)
    {
        for(int j=0; j<=n; j++) f[i][j] = f[i-1][j];
        int l = i, r = i;
        for(l=i; l>=1 && a[l]>=a[i]; l--); l++;
        for(r=i; r<=n && a[r]>=a[i]; r++); r--;
        int now = l ? f[i][l-1] : 0;
        for(int j=l; j<=r; j++) add(f[i][j], now), add(now, f[i-1][j]);
    }
    printf("%d\n", f[n][n]);

    return 0;
}
posted @ 2022-11-05 17:30  Catherine_leah  阅读(20)  评论(0编辑  收藏  举报
/* */