递归二分不分家~~~~
将解答的应用场景扩大到原问题的状态空间,并且扩展过程中每个步骤有相似性,则可以考虑递归和递推。
推导路线难以确定,从路线上反向回溯的遍历方式是递归。
假如我们能够做到:缩小问题状态空间规模,尝试求解规模缩小后的问题,找到规模缩小后的问题可以将答案扩展,如果失败去寻找其他变换路线直到确定无解。
其中规模缩小后的子问题用原问题解决是递归,而求解子问题失败寻找其他路径是回溯。
换言之,递归的基本单元是缩小、求解和拓展。
不按顺序当然是因为懒
对于一个计算机来说,它将函数的参数依次入栈,然后执行call(address)
语句。该指令将返回地址入栈,然后跳转到address。函数返回执行ret语句,将返回地址出栈,跳到该地址继续执行。局部变量在栈中复原,而作用范围超过此函数的变量、new或malloc分配空间则保存在堆中。栈指针、返回值、局部的运算通过寄存器完成。
因此,声明过多局部变量会造成栈溢出;非局部变量需要还原现场。
当然递归程序由此可以改写成非递归的,即手工模拟栈。
联赛似乎用不到的样子。。那就 挖个坑吧
递归实现指数型枚举:直接讨论选和不选,然后回溯即可。
递归实现组合型枚举:加一个剪枝
if(chosen.size() > m || chosen.size() + (n - x + 1) < m)
return;
递归实现排列型枚举:模板千千万
这题。。童年阴影啊
由唯一分解定理(这是啥别管,反正结论显然) A=∏ni=1pkii A = ∏ i = 1 n p i k i
则 AB=∏ni=1pB⋅kii A B = ∏ i = 1 n p i B ⋅ k i
然后约数之和为:
#include
#include
#include
using namespace std;
#define LL long long
#define X first
#define Y second
#define M 9901
#define mp make_pair
inline LL ksm(LL a, LL b) {
int ans = 1;
while(b) {
if(b & 1) ans = a * ans % M;
a = a * a % M;
b >>= 1;
}
return ans;
}
inline LL solve(LL p, LL c) {
if(!p) return 0;
if(!c) return 1;
if(c & 1)
return (1 + ksm(p, (c + 1 >> 1))) * solve(p, (c - 1) >> 1) % M;
else
return ((1 + ksm(p, c >> 1)) * solve(p, (c >> 1) - 1) % M + ksm(p, c)) % M;
}
vectorint , int> > vpi;
bool notprime[10003];
int prime[10003], cnt;
inline void getPrime() {
prime[1] = 2; cnt = 1;
for(int i = 3; i <= 10003; i += 2) {
if(notprime[i]) continue;
prime[++cnt] = i;
for(int j = 2; i * j < 10003; ++j)
notprime[i * j] = 1;
}
}
inline void fj(LL x) {
int qwq = 0;
for(int i = 1; i <= cnt; ++i) {
qwq = 0;
while(x % prime[i] == 0)
x /= prime[i], ++qwq;
if(qwq) vpi.push_back(mp(prime[i], qwq));
}
if(x != 1) vpi.push_back(mp(x, 1));
}
int main() {
getPrime();
int a, b;
cin>>a>>b;
//a %= M; b %= M;
fj(a);
//puts("here");
//a %= M, b %= M;
LL ans = 1;
for(int i = 0; i < vpi.size(); ++i) {
//cout<
ans = (ans * solve(vpi[i].first, b * vpi[i].second)) % M;
}
cout<
仍然是细节极多。。
本来这里应该是那道宽叔图。。但时间关系来不及了所以换一道类似的。。
#include
#include
#include
#include
using namespace std;
typedef unsigned long long LL;
#define MAXN 100003
#define INF 1e18
const string s = "What are you doing at the end of the world? Are you busy? Will you save us?";
const string s1 = "What are you doing while sending \"";
const string s2 = "\"? Are you busy? Will you send \"";
const string s3 = "\"?";
LL f[MAXN];
LL sb = s.size();
void dfs(LL x, LL y) {
// cout<
if(x == 0) {
cout<return ;
}
if(y < s1.size()) cout<else if(y < s1.size() + f[x - 1]) dfs(x - 1, y - s1.size());
else if(y < s1.size() + f[x - 1] + s2.size()) cout<1] - s1.size()];
else if(y < s1.size() + f[x - 1] + s2.size() + f[x - 1]) dfs(x - 1, y - s1.size() - f[x - 1] - s2.size());
else cout<1] - f[x - 1] - s1.size() - s2.size()];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
f[0] = s.size();
for(int i = 1; i <= 100000; ++i) {
f[i] = (f[i - 1] << 1) + s1.size() + s2.size() + s3.size();
if(f[i] > INF) f[i] = INF;
}
LL q, x ,y;
cin>>q;
while(q--) {
cin>>x>>y;
if(y > f[x]) cout<<".";
else dfs(x, y - 1);
}
return 0;
}
(我以后一定填这坑.jpg)
x或x的后继
while(l < r){
int mid = l + r >> 1;
if(a[mid] >= x) r = mid;
else l = mid + 1;
}
x或x的前驱
while(l < r){
int mid = l + r + 1 >> 1;
if(a[mid] <= x) l = mid;
else r = mid - 1;
}
果然我是那90%啊。注意配套mid取法。
正确做法:
先确定左右半段哪一个是可行区间,以及mid归属
然后选择上面的两种形式。
如果最终二分终止在越界下标,那就表明a中不存在。
当然。。我这种蒟蒻一般是写lower_bound和upper_bound的。。
两种做法:
一种是制定eps精度,一种是固定循环迭代次数。
单峰函数指的是只有唯一极大值点的函数,单谷函数指的是只有唯一极小值点的函数。
对于单峰函数的极值我们通常使用三分法。
在 [L,R] [ L , R ] 内选取 lmid l m i d 和 rmid r m i d 做三等分点,如果 f(lmid)<f(rmid) f ( l m i d ) < f ( r m i d ) ,则取 r=rmid r = r m i d ,否则取 l=lmid l = l m i d 。
然而这东西理论并没有用。。看栗子吧
最大化最小值是一个标志
然后我们就去实数域上二分T
判定的话暴力枚举一遍看看是否超m即可
二分最难的是写check啊。。
对答案二分,难点还是在check。
我们要求的是,是否存在一个不小于L的子段使得平均数不小于二分的值。所以首先对数列每一个元素减去平均数,转换为一个求限定长度的最大子段和的问题
我们可以维护一个前缀和,然后进行讨论:
max{S[i]−S[j]}=max{S[i]−min{S[j]}} m a x { S [ i ] − S [ j ] } = m a x { S [ i ] − m i n { S [ j ] } } ,其中 i−j>L i − j > L
而对 min{S[j]} m i n { S [ j ] } 的维护只需要在遍历过程中同步进行就可以,故总复杂度 O(nlogn) O ( n l o g n ) 。
#include
#include
using namespace std;
#define db double
int N, L;
db A[100003], cf[100003], sum[100003], mi, ans; //A要开double。。
bool check(db x) { //传参数传double。。
mi = 1e10, ans = -1e10;
for(int i = 1; i <= N; ++i) cf[i] = A[i] - x;
for(int i = 1; i <= N; ++i) sum[i] = sum[i - 1] + cf[i]; \
for(int i = L; i <= N; ++i) {
mi = min(mi, sum[i - L]);
ans = max(ans, sum[i] - mi);
}
if(ans >= 0) return true;
else return false;
}
int main() {
//freopen("test.out", "w", stdout);
scanf("%d%d", &N, &L);
for(int i = 1; i <= N; ++i) scanf("%lf", &A[i]);
db l = -1e6, r = 1e6, eps = 1e-5, mid;
while(r - l > eps) {
mid = (l + r) / 2;
for(int i = 1; i <= N; ++i) cf[i] = A[i] - mid;
for(int i = 1; i <= N; ++i) sum[i] = sum[i - 1] + cf[i];
mi = 1e10, ans = -1e10;
if(check(mid)) l = mid;
else r = mid;
}
cout << (int)(r * 1000) << endl;
return 0;
}
lyd原创题?
不是很懂。。第一次见到交互题
思路还是很显然的,假如我们前k个已经确定,那么就二分查找出一个合理的位置插入。复杂度 O(nlogn) O ( n l o g n )