2022NOIPA层联测21

B. 学数学

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

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

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

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

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

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),不会改了就直接交了个这个。。

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

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[i1][l1],再考虑l+1,有l+1是否被覆盖的情况,如果l+1被覆盖了还可以继续讨论l,方案数的增量就是f[i1][l]+f[i1][l1],发现可以求前缀和。

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 @   Catherine_leah  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
/* */
点击右上角即可分享
微信分享提示