2022NOIP A层联测15
阿哲,我把自己姓名全拼打错了(好像还不是第一次)
A
打表找规律,不会证
code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 65539;
int n, k;
vector<int>ans1,ans2;
int op[maxn];
int main(){
freopen("sandalphon.in","r",stdin);
freopen("sandalphon.out","w",stdout);
n = read(), k = read();
if(k == n){
printf("0\n");
return 0;
}
if(n == 1){
printf("1\n");
printf("1 1\n");
printf("1 2\n");
return 0;
}
int mx = 1 << (n - 2);
for(int i = 1; i <= mx; ++i)op[i] = 1;
for(int i = n - 2; i >= 1; --i){
for(int j = 1; j <= mx; j += (1 << i)){
for(int k = (1 << (i - 1)); k < (1 << i); ++k)op[j + k] ^= 1;
}
}
int len = 1 << (n - 1);
for(int i = 1; i <= mx; ++i)if(op[i]){
ans1.push_back(i * 4 - 3);
ans1.push_back(i * 4);
ans2.push_back(i * 4 - 1);
ans2.push_back(i * 4 - 2);
}else{
ans2.push_back(i * 4 - 3);
ans2.push_back(i * 4);
ans1.push_back(i * 4 - 1);
ans1.push_back(i * 4 - 2);
}
printf("1\n");
printf("%d ", len);for(int v : ans1)printf("%d ",v); printf("\n");
printf("%d ", len);for(int v : ans2)printf("%d ",v); printf("\n");
return 0;
}
B
写个分治,感觉次数很优,具体不会分析,但是过了
后来分析大概是 级别的,有个常数
做法考虑像线段树一样处理区间内部的连边,每次处理两个区间之间的连边
处理时对两个区间同步二分,具体来讲每次把较长区间分成两半,查询一半与较短区间的答案,通过区间间连边总数减出来另外一半的连边数,于是可以递归处理
记得充分利用线段树查询出的信息
code
#include"YuukiAsuna.h"
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<cassert>
using namespace std;
typedef pair<int, int> pii;
vector<pii>ans;
vector<int>S;
map<pii, int>mp;
void add(int l1, int r1, int l2, int r2, int c){
if(c <= 0)return;
int len1 = (r1 - l1), len2 = (r2 - l2);
if(len1 == 0 && len2 == 0){ans.push_back(pii(l1, l2)); return;}
if(len1 < len2){swap(l1, l2);swap(r1, r2);}
int m1 = (l1 + r1) >> 1;
S.clear();
for(int i = l1; i <= m1; ++i)S.push_back(i);
for(int i = l2; i <= r2; ++i)S.push_back(i);
int c1 = Query(S) - mp[pii(l1, m1)] - mp[pii(l2, r2)];
add(l1, m1, l2, r2, c1);
add(m1 + 1, r1, l2, r2, c - c1);
}
int solve(int l, int r){
if(l == r)return 0;
int mid = (l + r) >> 1;
S.clear();
for(int i = l; i <= r; ++i)S.push_back(i);
int c = mp[pii(l, r)] = Query(S);
if(c){
int cl = solve(l, mid);
int cr = solve(mid + 1, r);
assert(c - cl - cr >= 0);
add(l, mid, mid + 1, r, c - cl - cr);
}
return c;
}
vector<pii>Asuna(int n,int Q){
solve(1, n);
return ans;
}
C
发现取了若干次 后函数值无限趋近于某个常数,达到题目要求的精度也只需要大概四五十次操作,于是在线段树上每个节点维护取若干次 以后的区间总和,区间赋值可以直接做
这种写法需要注意常数。
考场打的就是这个,但是线段树打挂了,而且常数巨大
学习了一下小常数写法。
code
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 300005;
const double eps = 1e-6;
int n, m;
double a[maxn];
bool equ(double x, double y){return x + eps >= y && x - eps <= y;}
struct FHQ{
#define ls t[x].l
#define rs t[x].r
struct node{
int l, r, id, key, size;
double val, tagval, sum;
int tagcos;
bool allsame;
}t[maxn];
int root, cnt;
int New(int id, double val){
t[++cnt].val = val;
t[cnt].id = id;
t[cnt].key = rand();
t[cnt].tagval = maxn;
t[cnt].size = 1;
t[cnt].allsame = true;
return cnt;
}
void push_up(int x){
t[x].sum = t[ls].sum + t[rs].sum + t[x].val;
t[x].size = t[ls].size + t[rs].size + 1;
t[x].allsame = true;
if(ls)t[x].allsame &= t[ls].allsame & equ(t[ls].val, t[x].val);
if(rs)t[x].allsame &= t[rs].allsame & equ(t[rs].val, t[x].val);
}
void push_down(int x){
if(t[x].tagval != maxn){
t[ls].tagcos = t[rs].tagcos = 0;
t[ls].tagval = t[rs].tagval = t[x].tagval;
t[ls].val = t[rs].val = t[x].tagval;
t[ls].sum = t[ls].size * t[ls].val;
t[rs].sum = t[rs].size * t[rs].val;
t[x].tagval = maxn;
}
if(t[x].tagcos){
t[x].tagcos = min(t[x].tagcos, 50);
t[ls].tagcos += t[x].tagcos;
t[rs].tagcos += t[x].tagcos;
for(int i = 1; i <= t[x].tagcos; ++i)t[ls].val = cos(t[ls].val), t[rs].val = cos(t[rs].val);
t[ls].sum = t[ls].size * t[ls].val;
t[rs].sum = t[rs].size * t[rs].val;
t[x].tagcos = 0;
}
}
void split(int x, int &l, int &r, int id){
if(!x)return l = r = 0, void();
push_down(x);
if(id < t[x].id){split(t[x].l, l, t[x].l, id); r = x;}
else {split(t[x].r, t[x].r, r, id); l = x;}
push_up(x); return;
}
int merge(int x, int y){
if(!x || !y)return x | y;
if(t[x].key < t[y].key){push_down(x), t[x].r = merge(t[x].r, y), push_up(x); return x;}
else{push_down(y), t[y].l = merge(x, t[y].l), push_up(y); return y;}
}
void ins(int id, double val){root = merge(root, New(id, val));}
void modify(int L, int R, double v){
int l = 0, m = 0, r = 0;
split(root, l, r, R + 1);
split(l, l, m, L);
t[m].tagval = t[m].val = v;
t[m].tagcos = 0;
t[m].sum = t[m].size * v;
t[m].allsame = true;
root = merge(merge(l , m), r);
}
void dfs(int x){
if(t[x].allsame){
++t[x].tagcos;
t[x].val = cos(t[x].val);
t[x].sum = t[x].val * t[x].size;
return;
}
push_down(x);
if(ls)dfs(ls);
if(rs)dfs(rs);
t[x].val = cos(t[x].val);
push_up(x);
}
void modify_cos(int L, int R){
int l = 0, m = 0, r = 0;
split(root, l, r, R + 1);
split(l, l, m, L);
dfs(m);
root = merge(merge(l , m), r);
}
double query(int L, int R){
int l = 0, m = 0, r = 0;
split(root, l, r, R + 1);
split(l, l, m, L);
double ans = t[m].sum;
root = merge(merge(l , m), r);
return ans;
}
}t;
int main(){
// freopen("excalibur.in","r",stdin);
// freopen("excalibur.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; ++i)scanf("%lf",&a[i]);
for(int i = 1; i <= n; ++i)t.ins(i, a[i]), printf("%lf\n",t.query(1, n));
for(int i = 1; i <= m; ++i){
printf("%lf\n",t.query(1, n));
int op, l, r; scanf("%d%d%d",&op,&l,&r);
if(op == 1)t.modify_cos(l, r);
if(op == 2)printf("%.10lf\n",t.query(l, r));
if(op == 3){
double v; scanf("%lf",&v);
t.modify(l, r, v);
}
}
return 0;
}
D
又双叒叕是褐的
考虑按位确定答案,每次 的某一位是否能取
然后一个结论就是最优答案是匹配,不会证
调用函数
表示匹配 , 子树,到了 位,限制的答案下界为
返回最少多少点无法匹配
当 返回为 时,当前答案就是可行的
考虑如何进行匹配
- 的 位为
那么只能 配对,递归处理两棵子树加起来返回
- 的 位为
如果 且 那么 配对一定符合答案,返回
不存在的话,记
若 , 那么让 配对,返回无法配对的
如果 为一个下界,否则可取消部分配对,与 匹配一定合法,所以与 取
另外的下界是
所以直接对他们取max即可
code
// ubsan: undefined
// accoders
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<cctype>
#include<random>
#include<iostream>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 500005;
int n;
int tot, size[maxn * 32], son[maxn * 32][2];
int root = 1, cnt = 1;
void insert(int x){
int now = root;
for(int j = 30; j >= 0; --j){
++size[now];
int k = (x >> j) & 1;
if(!son[now][k])son[now][k] = ++cnt;
now = son[now][k];
}
++size[now];
}
int calc(int x, int y, int dep, int lim){
if(!x || !y)return size[x] | size[y];
if(dep < 0)return abs(size[x] - size[y]);
if((lim >> dep) & 1)return calc(son[x][0], son[y][1], dep - 1, lim) + calc(son[x][1], son[y][0], dep - 1, lim);
else{
if(size[son[x][0]] <= size[son[y][1]] && size[son[x][1]] <= size[son[y][0]])return size[y] - size[x];
if(size[son[x][0]] >= size[son[y][1]] && size[son[x][1]] >= size[son[y][0]])return size[x] - size[y];
int c0 = size[son[x][0]] + size[son[y][0]], c1 = size[son[x][1]] + size[son[y][1]];
if(c0 < c1){
int w = calc(son[x][1], son[y][1], dep - 1, lim);
return max(abs(size[x] - size[y]), w - c0);
}else{
int w = calc(son[x][0], son[y][0], dep - 1, lim);
return max(abs(size[x] - size[y]), w - c1);
}
}
}
int main(){
freopen("win.in","r",stdin);
freopen("win.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)insert(read());
int ans = 0;
for(int i = 30; i >= 0; --i){
if(!calc(1, 1, 30, ans + (1 << i)))ans += 1 << i;
}
printf("%d\n", ans);
return 0;
}
最近某越来越爱了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】