2022NOIP A层联测21 反转了 学数学 早该砍砍了 方格计数

[规律/数论]T2:给出n,正整数对\((x,y)\)的对数,满足\((x*y+1)|(x^2+y^2),x<=y<=n\),(n<=1e18)

考场

把表打出来发现\((a,a^3)\)是通解,8,30开始的特殊解就找不到规律了

正解

【1】

结论吧,\((x,y)\)是解,\((x*y+1)/(x^2+y^2)=K\)那么\(y,x-K*y\)也是一组解,倒推回去

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define chu printf
#define ll long long
#define ull unsigned long long
#define rint register int
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 1e7;
ull ans[N], mx;  //最多多少?不知道
int T;
ull q[(int)1e5 + 100];
int main() {
    // freopen("1.in","r",stdin);
    //  freopen("1.out","w",stdout);
    freopen("math.in", "r", stdin);
    freopen("math.out", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);这玩意怎么用?
    T = re();
    for (rint i = 1; i <= T; ++i) q[i] = re(), mx = max(q[i], mx);
    ans[++ans[0]] = 1;
    for (ull i = 2; i * i * i <= mx; ++i) {
        // chu("d\n");
        ull x = i, y = i * i * i;
        ull res = i * i;
        while (1) {
            // chu("")
            ans[++ans[0]] = max(x, y);
            // if(x>y)
            // {
            //     chu("(%llu %llu)shit\n",x,y);exit(0);
            // }
            // chu("a sol:%llu %llu\n",x,y);
            ull tmp = x;
            x = y;
            if (res > (mx + tmp) / y)
                break;
            // if(x>1e18)chu("Y:%llu\n",y);
            y = (res * y - tmp);
        }
    }
    sort(ans + 1, ans + 1 + ans[0]);
    for (rint i = 1; i <= T; ++i) {
        int x = upper_bound(ans + 1, ans + 1 + ans[0], q[i]) - ans - 1;
        chu("%d\n", x);
    }
    return 0;
}
/*
1
924297727909251471
*/

【2】

发现首尾相接的规律,8,30,112(rel=4),\(fi=x^2*f_{i-1}-x*k\),对于每一组通解开始的特殊解都可以这么推下去

[区间DP]T3:你有无限次机会选择一个区间[l,r],使得\(\forall ai=min_{ai},l<=i<=r\),求最终的序列形态数量。(给出保证是排列,n<=3000)

考场

发现操作上界是n-1,直接暴力跑。
对于1,2,3,4,5....n的排列,发现i位置是x,那么可以从i-1位置是1--x转移过来,而且x<=i,所以倒推一下,维护个后缀和.\(f[i][j]=\sum_{k<=i-1}^{}f[i-1][k],表示填到i位置数字是j的方案数\)

正解

这个东西很有本质,比如你发现i位置可以是a[j]当且仅当,a[j]是i--j的区间最小值,和其他的没有关系,j位置可以是其他的<a[j]的值,那么预处理出li,ri表示i最小值控制的区间,问题转化成给定若干不定长区间,可以不乱序任意排布,求最终形态方案数。
枚举每一个区间的作用,枚举区间i的覆盖范围[l,r],那么i的贡献加上没有i的贡献的方案数就是\(\sum_{Li<=l<=Ri}^{}\sum_{l<-r<=Ri}f[i][j]=f[i-1][j]+f[i-1][l]\),
发现每次i转移都是一段连续区间,直接维护前缀和就行。
还有更优美的形式,就是
\(f_i=f_{i-1}+fi\),直接覆盖第一维转移,
因为发现直接继承i-1状态,前缀刚好可以维护

点击查看代码
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
#define chu printf
#define ll long long
#define ull unsigned long long
#define rint register int
inline ll re() {
    ll x = 0, h = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            h = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * h;
}
const int N = 3010;
const int mod = 1e9 + 7;
int f[N][N], sum[N][N], n, cor[N], L[N], R[N], stk[N], top;
inline void upd(int& a, int b) {
    a = a + b;
    a = ((a < 0 ? (a + mod) : (a >= mod ? (a - mod) : a)));
}
int main() {
    // freopen("1.in","r",stdin);
    // freopen("1.out","w",stdout);
    freopen("cut.in", "r", stdin);
    freopen("cut.out", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);这玩意怎么用?
    n = re();
    for (rint i = 1; i <= n; ++i) L[i] = 1, R[i] = n;
    for (rint i = 1; i <= n; ++i) {
        cor[i] = re();
        while (top > 0 && cor[stk[top]] > cor[i]) {
            R[stk[top--]] = i - 1;
        }
        L[i] = stk[top] + 1;
        stk[++top] = i;
    }
    sum[0][0] = 1;
    for (rint i = 0; i <= n; ++i) sum[0][i] = 1;
    f[0][0] = 1;
    for (rint i = 1; i <= n; ++i) {
        // chu("l:%d r:%d\n",L[i],R[i]);
        for (rint j = L[i]; j <= R[i]; ++j) {
            upd(f[i][j], sum[i - 1][j - 1] - ((L[i] >= 2) ? sum[i - 1][L[i] - 2] : 0));
        }
        for (rint j = 1; j <= n; ++j) upd(f[i][j], f[i - 1][j]);  // chu("f[%d][%d]:%d\n",i,j,f[i][j]);
        sum[i][0] = 1;
        for (rint j = 1; j <= n; ++j) upd(sum[i][j], sum[i][j - 1] + f[i][j]);
    }
    chu("%d", f[n][n]);
    return 0;
}
/*
6
3 5 6 2 1 4
*/
posted on 2022-11-06 07:23  HZOI-曹蓉  阅读(19)  评论(0编辑  收藏  举报