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
*/