NOIP模拟19/07/06

WOJ4059 斐波那契

每次贪心选出最长的一段, 枚举45个斐波那契数, 如果存在 fib[i] - 当前数 出现, 就不合法

说一下我考场的做法

预处理每个数右边第一个与它组成斐波那契的位置 r[i]

f[i]=min(f[j]+1)(check(j+1,i)=true) 

可以单调队列优化

如何判断呢? 如果合法, 那么(j+1 -- i) 区间的最小的r[i] 应该大于i, 写个st表就可以了

#include<bits/stdc++.h>
#define N 100050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt<<1) + (cnt<<3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int n, a[N], tot, ans; ll fi[N];
int main(){
	n = read();
	for(int i=1; i<=n; i++) a[i] = read();
	fi[1] = 1; fi[2] = 2;
	for(int i=3; ;i++){
		fi[i] = fi[i-1] + fi[i-2]; if(fi[i] > 2e9) break; tot = i;
	}  
	for(int l = 1, r; l <= n; l = r){ set<int> S;
		for(r = l; r <= n; r++){
			int flag = 0; for(int i=1; i<=tot; i++)
				if(S.count(fi[i] - a[r])){ flag = 1; break;}
			if(flag) break; S.insert(a[r]);
		} ans ++;
	} printf("%d", ans);
	return 0;
}

WOJ4060 序列

居然没有想到 ! 将 l1 -- r1 的限制转换成 1 -- l1-1, 和 1 -- r1 的

于是考虑如何求出中位数 <= k的方案数

想到了 middle 那道题, 将 <= k 的赋成 1, 否则 -1, 那么 1 的个数 >= -1 的个数就合法

求一个前缀和sum, 当前 i 就是查 i - Maxlen ---- i - Minlen 之间sum < sumi 的个数, 主席树就可以了

#include<bits/stdc++.h>
#define N 200050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt<<1) + (cnt<<3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
const int S = 100000;
int n, a[N], m, sum[N], rt[N], tot;
struct Node{ int ls, rs, val;} t[N * 20];
void Build(int &x, int l, int r){
	x = ++tot; if(l == r) return; int mid = (l+r) >> 1;
	Build(t[x].ls, l, mid); Build(t[x].rs, mid+1, r);
}
void Insert(int &x, int last, int l, int r, int pos){
	x = ++tot; t[x] = t[last]; t[x].val ++;
	if(l == r) return; int mid = (l+r) >> 1;
	if(pos <= mid) Insert(t[x].ls, t[last].ls, l, mid, pos);
	else Insert(t[x].rs, t[last].rs, mid+1, r, pos);
}
int Quary(int a, int b, int l, int r, int L, int R){
	if(!a) return 0; if(L<=l && r<=R) return t[a].val - t[b].val;
	int mid = (l+r) >> 1, ans = 0;
	if(L<=mid) ans += Quary(t[a].ls, t[b].ls, l, mid, L, R);
	if(R>mid) ans += Quary(t[a].rs, t[b].rs, mid+1, r, L, R);
	return ans;
} 
ll Get(int A, int Minlen, int Maxlen){
	for(int i=1; i<=tot; i++) t[i].ls = t[i].rs = t[i].val = 0;
	memset(rt, 0, sizeof(rt)); tot = 0;
	for(int i=1; i<=n; i++){
		if(a[i] <= A) sum[i] = 1; else sum[i] = -1;
		sum[i] += sum[i-1];
	} Build(rt[0], 1, n + S); Insert(rt[0], rt[0], 1, n + S, 0 + S);
	for(int i=1; i<=n; i++) Insert(rt[i], rt[i-1], 1, n + S, sum[i] + S);
	ll ans = 0;
	for(int i=1; i<=n; i++){
		if(i - Minlen < 0) continue;
		int r = i - Minlen, l = max(i - Maxlen, 0);
		ans += (ll)Quary(rt[r], l ? rt[l-1] : 0, 1, n + S, 1, sum[i] + S);
	} return ans;
}
int main(){
	n = read();
	for(int i=1; i<=n; i++) a[i] = read();
	m = read();
	while(m--){
		int l1 = read(), r1 = read(), l2 = read(), r2 =read();
		printf("%lld\n", Get(r1, l2, r2) - Get(l1-1, l2, r2));
	} return 0;
}

WOJ4061栅栏

不能直接左下+1, 右上+1, 左上-1, 右下-1, 因为如果 i, j 分别在不同的矩阵就会炸

考虑将原问题转换, 相当于求是否存在一个矩阵, 使i在里面 但 j不在里面

有一个玄学做法, 就是给每个矩阵rand一个值val, 插入树状数组

如果 i 这个点的 val 的异或等于 j 的异或那么大概率i, j没有被隔开

#include<bits/stdc++.h>
#define N 2050
using namespace std;
typedef long long ll;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1;}
	while(isdigit(ch)) cnt = (cnt<<1) + (cnt<<3) + (ch-'0'), ch = getchar();
	return cnt * f;
}
int Rand(){ return (rand() << 15) | rand();}
#define mp make_pair
#define pa pair<int, int>
int n, m, S, q, c[N][N];
map<pa, int> M;
void Add(int x, int y, int val){
	for(int i=x; i<=S; i+=i&-i)
		for(int j=y; j<=S; j+=j&-j)
			c[i][j] ^= val;
}
int Ask(int x, int y){
	int ans = 0;
	for(int i=x; i; i-=i&-i)
		for(int j=y; j; j-=j&-j)
			ans ^= c[i][j];
	return ans;
}
int main(){
	n = read(), m = read(), q = read(); S = max(n, m) + 1;
	while(q--){
		int op = read(), x1 = read(), y1 = read(), x2 = read(), y2 = read();
		if(op == 1){ 
			int val = 0;
			M[mp(x1 * S + y1, x2 * S + y2)] = val = Rand();
			Add(x1, y1, val); Add(x2+1, y1, val); Add(x1, y2+1, val); Add(x2+1, y2+1, val);
		}
		if(op == 2){
			int val = M[mp(x1 * S + y1, x2 * S + y2)];
			Add(x1, y1, val); Add(x2+1, y1, val); Add(x1, y2+1, val); Add(x2+1, y2+1, val);
		}
		if(op == 3){
			if(Ask(x2, y2) ^ Ask(x1, y1)) printf("No\n");
			else printf("Yes\n");
		}
	} return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值