zoukankan      html  css  js  c++  java
  • [Contest on 2021.8.26] 打脸,有时是在一瞬之间

    ( ext{CF1045G AI robots})

    关于这道题

    模拟赛的时候,旁边的 (sf{ET}) 秃然说:"这不是 ( m cdq) 分治吗?"

    我当机立断地否定了她的想法,并且表达了 "你写出来算我输" 的意愿。然后…

    键盘啪啪的,很快啊!她就把这题切了!

    我则 "点到为止",写了个线段树优化建图,关键是好像是错的…

    哭了。但是我是真不会

    解法

    "两个机器人互相看见" 这个条件是很难维护的,因为 ( m cdq) 分治只支持单向的查询。

    别急着放弃啊!我们能否将 "双向" 改成 "单向" 呢?考虑两个机器人 (i,j),如果 (r_i>r_j)(j) 能够看到 (i),那么 (i) 一定能看到 (j)!这样我们将 (r_i) 从大到小排序,就转化成了 "单向" 的问题。

    我们可以设想一下:在算法内部,右半边的 (r) 都小于左半边,每个半边则满足 (q) 从小到大排序。用双指针将智商合法的左半边机器人加入某个数据结构,在右半边查询。显然,在将机器人加入数据结构时,应当加入 (x) 坐标,方便后面的查询。所以我们还要预处理一下每个机器人能看到的区间范围。

    维护 (u-vle k) 很好维护,(|u-v|le k) 呢?其实是类似的。 就像这样:

    while(L<=mid and s[i].q-s[L].q>k)
    	add(s[L].x,-1),++L;
    while(R<mid and s[R+1].q-s[i].q<=k)
    	++R,add(s[R].x,1);
    

    就算 s[R+1].q-s[i].q 是负数且相距 (>k),前面的 while() 实际上已经减去这种情况了。

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    } 
    
    #include <algorithm>
    using namespace std;
    
    const int maxn=1e5+5;
    
    int n,k,X[maxn],len;
    int c[maxn];
    long long ans;
    struct node {
    	int x,R,q,l,r;
    	
    	bool operator < (const node &t) const {
    		return R>t.R;
    	}
    } s[maxn];
    
    bool cmp(node a,node b) {
    	return a.q<b.q;
    }
    
    int lowbit(int x) {
    	return x&-x;
    }
    
    void add(int x,int k) {
    	while(x<=len)
    		c[x]+=k,
    		x+=lowbit(x);
    }
    
    int ask(int x) {
    	int r=0;
    	while(x)
    		r+=c[x],
    		x-=lowbit(x);
    	return r;
    }
    
    void cdq(int l,int r) {
    	if(l==r) return;
    	int mid=l+r>>1;
    	cdq(l,mid); cdq(mid+1,r);
    	int L=l,R=l-1;
    	for(int i=mid+1;i<=r;++i) {
    		while(L<=mid and s[i].q-s[L].q>k)
    			add(s[L].x,-1),++L;
    		while(R<mid and s[R+1].q-s[i].q<=k)
    			++R,add(s[R].x,1);
    		ans+=ask(s[i].r)-ask(s[i].l-1); 
    	}
    	for(int i=L;i<=R;++i)
    		add(s[i].x,-1);
    	sort(s+l,s+r+1,cmp);
    }
    
    int main() {
    	n=read(9),k=read(9);
    	for(int i=1;i<=n;++i)
    		X[i]=s[i].x=read(9),
    		s[i].R=read(9),
    		s[i].q=read(9);
    	sort(s+1,s+n+1);
    	sort(X+1,X+n+1);
    	len=unique(X+1,X+n+1)-X-1;
    	for(int i=1;i<=n;++i) {
    		s[i].l=lower_bound(X+1,X+len+1,s[i].x-s[i].R)-X;
    		s[i].r=upper_bound(X+1,X+len+1,s[i].x+s[i].R)-X-1;
    		s[i].x=lower_bound(X+1,X+len+1,s[i].x)-X;
    	}
    	cdq(1,n);
    	print(ans,'
    ');
    	return 0;
    }
    

    ( ext{NAND})

    题目描述

    初始时你有一个空序列,之后有 (n) 个操作。

    操作分为以下两种:

    • 在序列末尾插入一个元素 (x)
    • 定义与非运算:(a ext{ nand }b=!(a ext{ and }b))( ext{nand}(l,r))(l)(r) 的与非和。询问 (igoplus_{i=l}^r ext{nand}(l,i))

    强制在线(nle 4cdot 10^6)。操作一个数 (le 3900000),操作二个数 (le 10^5)

    解法

    ( m md)( m std) 的代码真的不当人啊!真的是什么不好懂写什么。哭了。

    首先肯定想到用线段树维护,但是打表发现,与非运算没有结合律,考虑到本题数字只有 (0,1),我们不妨枚举首个数字 (x),维护 (val_o=x ext{ nand }a_l ext{ nand }a_{l+1} ext{ nand }... ext{ nand }a_r)

    合并两个区间 (a,b),就有:

    [ans_x=ans_{a,x}oplus (val_{a,x} ext{ nand }a_{ ext{mid}+1})oplus...oplus (val_{a,x} ext{ nand }a_{ ext{mid}+1} ext{ nand }... ext{ nand } a_r) ]

    [=ans_{a,x}oplus ans_{b,val_{a,x}} ]

    这样就可以合并了。

    不过,操作一的个数看上去就是来卡你的 (log) 的,我们能否将它的复杂度减到 (mathcal O(1)) 呢?

    while(o!=1 and (o&1)) {
    	o>>=1;
    	t[o]=Merge(t[o<<1],t[o<<1|1]);
    }
    

    当这个节点是右儿子的时候,就一直往上合并。这样一路向上合并的时候,顺带把已经合并好的左儿子一起合并。由于每个点只会被合并一次,所以均摊是 (mathcal O(1)) 的。

    不过为啥这是对的捏?考虑题目的性质:你插好多,它问好多。所以没被右儿子更新的区间此时一定查不完整,所以查询时不会出错。


    另外再提一嘴与非运算对异或运算的分配律问题。当时就是被这个坑了很久

    即计算 (a ext{ nand }igoplus_{i=1}^n v_i)打表发现

    • (n) 为奇数时。( ext{Ans}=igoplus_{i=1}^n a ext{ nand }v_i)
    • (n) 为偶数时。( ext{Ans}=!igoplus_{i=1}^n a ext{ nand }v_i)

    代码

    #include <cstdio>
    #define print(x,y) write(x),putchar(y)
    
    template <class T>
    inline T read(const T sample) {
    	T x=0; char s; bool f=0;
    	while((s=getchar())>'9' or s<'0')
    		f|=(s=='-');
    	while(s>='0' and s<='9')
    		x=(x<<1)+(x<<3)+(s^48),
    		s=getchar();
    	return f?-x:x;
    }
    
    template <class T>
    inline void write(const T x) {
    	if(x<0) {
    		putchar('-'),write(-x);
    		return;
    	}
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    } 
    
    #include <iostream>
    using namespace std;
    
    const int maxn=4e6+5;
    
    struct node {
    	int val[2],ans[2];
    } t[maxn<<2];
    int p[maxn],a[maxn];
    
    node Merge(node a,node b) {
    	node r;
    	r.val[0]=b.val[a.val[0]];
    	r.val[1]=b.val[a.val[1]];
    	r.ans[0]=a.ans[0]^b.ans[a.val[0]];
    	r.ans[1]=a.ans[1]^b.ans[a.val[1]];
    	return r;
    }
    
    node ask(int o,int l,int r,int L,int R) {
    	if(l>=L and r<=R) return t[o];
    	int mid=l+r>>1;
    	if(R<=mid) return ask(o<<1,l,mid,L,R);
    	if(L>mid) return ask(o<<1|1,mid+1,r,L,R);
    	node f=ask(o<<1,l,mid,L,R);
    	f=Merge(f,ask(o<<1|1,mid+1,r,L,R));
    	return f;
    }
    
    void build(int o,int l,int r) {
    	if(l==r) return (void)(p[l]=o);
    	int mid=l+r>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    }
    
    int main() {
    	int lst=0,op,x,y,n=0,T=read(9);
    	build(1,1,T);
    	for(int i=1;i<=T;++i) {
    		op=read(9),x=read(9);
    		if(op==1) {
    			x^=lst;
    			a[++n]=x;
    			int o=p[n];
    			if(!x) {
    				t[o].val[0]=t[o].val[1]=1;
    				t[o].ans[0]=t[o].ans[1]=1;
    			}
    			else {
    				t[o].val[0]=t[o].ans[0]=1;
    			}
    			while(o!=1 and (o&1)) {
    				o>>=1;
    				t[o]=Merge(t[o<<1],t[o<<1|1]);
    			}
    		}
    		else {
    			y=read(9);
    			if(lst) {
    				x=n-x+1;
    				y=n-y+1;
    				swap(x,y);
    			}	
    			if(x==y) {
    				print(lst=a[x],'
    ');
    				continue;
    			}
    			node r=ask(1,1,T,x+1,y);
    			print(lst=r.ans[a[x]]^a[x],'
    ');
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    码农自白:这样成为谷歌工程师
    Vim命令合集
    应该知道的Linux技巧
    在Ubuntu上建立Arm Linux 开发环境
    Linux 下socket通信终极指南(附TCP、UDP完整代码)
    Socket通信原理和实践
    用 gdb 调试 GCC 程序
    Quartz学习记录
    shiro学习记录(三)
    shiro学习记录(二)
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15192331.html
Copyright © 2011-2022 走看看