加载中…

返回上一页

2022NOIP A层联测21

下发文件(密码为原 accoders 比赛密码)

反转了

有一个很重要的条件,它决定了这个题并不需要平衡树:

𝑎𝑖 < 𝑎𝑖 + 1 (∀ 1 ≤ 𝑖 < 𝑘).

每次翻转它们的相对位置不会改变,那么可以维护一个双端队列,如果当前前面的已被翻转(就是反着的),就从前面插,否则从后面插.

注意插的时候要再额外维护一个 tag,代表其 push 进去的时候是正的还是反的. 翻转奇数次后 push 进去的那一定是反的啦.

其实核心思想也就是把每一段维护一个函数.

我这里用的 std::deque,一般情况下最好还是手写队列保险,因为某个人因为使用了 std::deque 最终无缘国赛金牌. 试了一下大数据跑得挺快于是就铤而走险试一下.

点击查看代码
#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 500005
#define pll pair<pair<ll,ll>,bool>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,k,a,ls=1;
char s[maxn];
bool isrev;
deque<pll> q;
int main()
{
	freopen("reverse.in","r",stdin); freopen("reverse.out","w",stdout);
	n=read();k=read();scanf("%s",s+1); while(k--)
	{
		a=read(); isrev?q.push_front({ {ls,a},1 }):q.push_back({ {ls,a},0 });
		isrev^=1;ls=a+1;
	}
	if(isrev) while(!q.empty())
	{
		rll st=q.back().first.second,ed=q.back().first.first;
		if(!q.back().second) for(rll i=st;i>=ed;i--) putchar(s[i]);
		else for(rll i=ed;i<=st;i++) putchar(s[i]); q.pop_back();
	}
	else while(!q.empty())
	{
		rll st=q.front().first.first,ed=q.front().first.second;
		if(!q.front().second) for(rll i=st;i<=ed;i++) putchar(s[i]);
		else for(rll i=ed;i>=st;i--) putchar(s[i]); q.pop_front();
	}
	for(rll i=ls;i<=n;i++) putchar(s[i]);
	return 0;
}

学数学

韦达定理是个好东西.

首先通过式子 x2 + y2 = k(xy + 1) 可以发现每个数的初解一定符合 (x , x3).

利用韦达定理,如果解 (x , y) 是一组解,那么把式子倒一下,(x , kx - y) 也是一组解.

有人可能会问这个 k 怎么求呢?其实发现它是个定值. 把解 (x , x3) 代回原来的那个式子,神奇地发现 x2 + x6 = k(x4 + 1). 那么这个 k 就显然啦.

k = x2,其中 x 为初解.

发现每个 x 在更新到下一个解的时候,这个玩意(k)不变.

把所有的都按照这种方法,不断进行答案迭代,然后计算答案.

迭代答案
inline ll f(rll x)
{
	rll a=x,b=x*x*x,k=x*x,t,ans=0;
	while(b<=n) { ans++,t=a,a=b; if((ld)b*k-t>n) break; b=b*k-t; } return ans;
}

然后得到了 80 分.

优化一下,先把 1 ~ 1e6 的所有答案都提前计算出来(因为 1e18 开三次方根是 1e6),存到一个 vector 里,每次二分查找答案.

也可以去找到每个数对应有多少个答案,二分这个,但是没必要了,因为就 1e6 个数嘛.

赛时打的表
a b*a²+b² ab+1
1 1*2 2
2 8*68 17
3 27*738 82
4 64*4112 257
5 125*15650 626
6 216*46692 1297
7 343*117698 2402
8 30*964 241
8 512*262208 4097
9 729*531522 6562
10 1000*1000100 10001
11 1331*1771682 14642
12 1728*2986128 20737
13 2197*4826978 28562
14 2744*7529732 38417
15 3375*11390850 50626
16 4096*16777472 65537
17 4913*24137858 83522
18 5832*34012548 104977
19 6859*47046242 130322
20 8000*64000400 160001
21 9261*85766562 194482
22 10648*113380388 234257
23 12167*148036418 279842
24 13824*191103552 331777
25 15625*244141250 390626
26 17576*308916452 456977
27 240*58329 6481
27 19683*387421218 531442
28 21952*481891088 614657
29 24389*594824162 707282
30 112*13444 3361
30 27000*729000900 810001
31 29791*887504642 923522
32 32768*1073742848 1048577
33 35937*1291469058 1185922
34 39304*1544805572 1336337
35 42875*1838266850 1500626
36 46656*2176783632 1679617
37 50653*2565727778 1874162
38 54872*3010937828 2085137
39 59319*3518745282 2313442
40 64000*4096001600 2560001
41 68921*4750105922 2825762
42 74088*5489033508 3111697
43 79507*6321364898 3418802
44 85184*7256315792 3748097
45 91125*8303767650 4100626
46 97336*9474299012 4477457
64 1020*1044496 65281
112 418*187268 46817
125 3120*9750025 390001
216 7770*60419556 1678321
240 2133*4607289 511921
343 16800*282357649 5762401
418 1560*2608324 652081
512 32760*1073479744 16773121
729 59040*3486253041 43040161
1000 99990*9999000100 99990001
1020 16256*265297936 16581121
1560 5822*36329284 9082321
2133 18957*363917538 40435282
3120 77875*6074250025 242970001
5822 21728*506001668 126500417
21728 81090*7047694084 1761923521
65
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 500005
#define ld long double
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll t,n=1e18,mx=pow(1e18,(ld)1/3),ans;
vector<ll> zk;
inline ll f(rll x)
{
	rll a=x,b=x*x*x,k=x*x,t;
	while(b<=n) { zk.emplace_back(max(a,b)); t=a,a=b; if((ld)b*k-t>n) break; b=b*k-t; } return ans;
}
int main()
{
	freopen("math.in","r",stdin); freopen("math.out","w",stdout);
	for(rll i=2;i<=mx;i++) f(i);sort(zk.begin(),zk.end());
	// for(rll i=0;i<=100;i++) cout<<zk[i]<<' ';
	t=read(); while(t--)
	{
		n=read();// mx=__builtin_powl(n,(ld)1/3);
		write(upper_bound(zk.begin(),zk.end(),n)-zk.begin()+1/*-1+1*/);putn;
	}
	return 0;
}

早该砍砍了

更新成一个合法的方案且不重复,当且仅当每次更新的区间不存在包含情况. 其他的均为一种新的情况(因为高度互不相同). 其实这么说也不是很准确

dp[i][j] 表示考虑了前 i 个数,对于前 i 个数到第 j 个数时合法的方案数(有些拗口,多念念).

念熟了之后,发现转移方程显然:

dp[i][j] = dp[i][j - 1] + dp[i - 1][j].

边界 dp[1][0] = 1.

发现这个转移存在漏洞,会把状态算重,因为一个最小值只需要计算一次.

维护一个前缀,mn[i][j] 表示从 i 开始到 j 的最小值. 当最小值符合当前转移这个数的时候再进行转移,否则将这个状态清空(dp 值设为 0).

转移的时候由于要把整个数列的 dp 值全部转移,最后累和的时候把所有合法的 dp[n][i] 加起来,或者再向下 dp 一次(注意要把 h[n + 1] 设为 n + 1)之后的 dp[n + 1][n + 1],这两者选其一都是答案.

这样就以 n2 的优秀复杂度优雅地通过了这道题.

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define rg register
#define rll rg ll
#define maxn 3002
#define mod 1000000007
#define pll pair<ll,ll>
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rg bool f=0;rll x=0;rg char ch=getchar(); while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar(); return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x; if(x>9) write(x/10); putchar(x%10|'0'); }
ll n,h[maxn];
ll mn[maxn][maxn],dp[maxn][maxn];
int main()
{
	freopen("cut.in","r",stdin); freopen("cut.out","w",stdout);
	n=read(); for(rll i=1;i<=n;i++) h[i]=read(); h[0]=h[n+1]=n+1;
	for(rll i=0;i<=n+1;i++) { mn[i][i]=h[i]; for(rll j=i+1;j<=n+1;j++) mn[i][j]=min(mn[i][j-1],h[j]); }
	dp[1][0]=1; for(rll i=1;i<=n+1;i++)
	{
		for(rll j=1;j<=n+1;j++) dp[i][j]=(dp[i][j-1]+dp[i-1][j])%mod;
		for(rll j=1;j<=n+1;j++) if(mn[min(i,j)][max(i,j)]^h[j]) dp[i][j]=0;
	}
	write(dp[n+1][n+1]);
	return 0;
}

方格计数

没改.

posted @ 2022-11-05 15:30  1Liu  阅读(28)  评论(0)    收藏  举报