zoukankan      html  css  js  c++  java
  • 【LOJ502】[LibreOJ β Round] ZQC 的截图 (随机化)

    真的是神仙题目啊……

    题目

    LOJ502

    官方题解

    我认为官方题解比我讲得好。

    分析

    这是一道蒙特卡洛算法的好题

    上面那个奇奇怪怪的词是从官方题解里看到的,意思大概就是随机化算法 …… ?

    一句话题意:给一棵有根树,每个点有一种颜色。每次加一个叶子,询问从根到这个叶子的路径上是不是所有颜色的出现次数都是 (3) 的倍数。如果不是,再询问是不是只有一个点不是 (3) 的倍数。如果只有一个点不是 (3) 的倍数,再询问这个点的编号。强制在线。

    思考部分分似乎对想到正解没有什么帮助(谁能想到这题是随机化啊 wq ),所以直接来说正解。

    考虑给每种颜色分配一个长为 (w) 的随机向量作为权值(我取(w=40) 。如果你像我一样没学过高中数学对向量不熟悉,就理解成一个长为 (w) 的数组好了)。把根到叶子路径上所有点的颜色的权值相加,每一维分别在模 (3) 意义下进行。如果和的每一维(可以理解成数组的每一个元素)都是 (0) ,那么说明所有颜色的出现次数都是 (3) 的倍数。这个算法的正确率证明如下:

    在模域下,若干个随机的数加起来的结果是一个随机数(这里的「随机」指模域内每个数概率相等,下同);在模质数域下,随机数乘上一个非 (0) 整数仍然是随机数。而由于一堆随机数按特定顺序拼在一起就是一个随机向量,所以在模质数域下随机向量的加、数乘(乘数非 (0) )的结果仍然是随机向量。回到本题。如果不是所有颜色的出现次数都是 (3) 的倍数,那么和就相当于所有出现次数不是 (3) 的倍数的颜色的权值的 (1) 倍或 (2) 倍之和。由于 (3) 是质数,根据上述性质,和仍然是一个随机向量。这个随机向量是 (vec{0}) 的概率是 (frac{1}{3^w}) ,非常小。

    如何判断是否只有一种颜色不是 (3) 的倍数呢?如果只有一种颜色不是 (3) 的倍数,那么和就是这种颜色的权值的 (1) 倍或 (2) 倍。那么直接判断一下是否存在一种颜色的权值是和的 (1) 倍或 (2) 倍(模 (3) 意义下乘 (4) 相当于乘 (1) )即可。用哈希表维护。

    这样做的正确率如何呢?如果有不止一种颜色出现的次数不是 (3) 的倍数,和就是一个随机向量。和与一种颜色的权值的 (1) 倍或 (2) 倍相等的概率为 (1-(1-frac{2}{3^w})^{n}) 。直观感受一下感觉挺小的(毕竟 (3^{40}=12,157,665,459,056,928,801‬)

    代码

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cctype>
    #include <cstdlib>
    using namespace std;
    
    namespace zyt
    {
    	template<typename T>
    	inline bool read(T &x)
    	{
    		char c;
    		bool f = false;
    		x = 0;
    		do
    			c = getchar();
    		while (c != EOF && c != '-' && !isdigit(c));
    		if (c == EOF)
    			return false;
    		if (c == '-')
    			f = true, c = getchar();
    		do
    			x = x * 10 + c - '0', c = getchar();
    		while (isdigit(c));
    		if (f)
    			x = -x;
    		return true;
    	}
    	template<typename T>
    	inline void write(T x)
    	{
    		static char buf[20];
    		char *pos = buf;
    		if (x < 0)
    			putchar('-'), x = -x;
    		do
    			*pos++ = x % 10 + '0';
    		while (x /= 10);
    		while (pos > buf)
    			putchar(*--pos);
    	}
    	typedef unsigned long long ull;
    	const int N = 1e6 + 10, M = 2e6 + 10;
    	int lastans = 0, n, m;
    	struct bitset_3
    	{
    		const static int B = 32;
    		ull data;
    		bitset_3(const ull &b = 0)
    			: data(b) {}
    		void set(const int a, const int x)
    		{
    			data &= ~((1ULL << (a << 1)) | (1ULL << (a << 1 | 1)));
    			data |= (ull(x & 3) << (a << 1));
    		}
    		int query(const int a) const
    		{
    			return (data >> (a << 1)) & 3;
    		}
    		void rand()
    		{
    			for (int i = 0; i < B; i++)
    				set(i, std::rand() % 3);
    		}
    		bool operator == (const bitset_3 &b) const
    		{
    			return data == b.data;
    		}
    		bitset_3 operator ^ (const bitset_3 &b) const
    		{
    			bitset_3 ans;
    			for (int i = 0; i < B; i++)
    				ans.set(i, (query(i) + b.query(i)) % 3);
    			return ans;
    		}
    		ull to_ull() const
    		{
    			return data;
    		}
    		bool empty() const
    		{
    			return !data;
    		}
    	}w[N], v[M];
    	namespace Hash_Table
    	{
    		const int P = 999983;
    		struct edge
    		{
    			bitset_3 to;
    			int w, next;
    		}e[N];
    		int head[P], ecnt;
    		void init()
    		{
    			memset(head, -1, sizeof(head));
    			ecnt = 0;
    		}
    		void add(const int a, const bitset_3 &b, const int c)
    		{
    			e[ecnt] = (edge){b, c, head[a]}, head[a] = ecnt++;
    		}
    		void add(const bitset_3 &b, const int c)
    		{
    			add(b.to_ull() % P, b, c);
    		}
    		int query(const bitset_3 &b)
    		{
    			for (int i = head[b.to_ull() % P]; ~i; i = e[i].next)
    				if (e[i].to == b)
    					return e[i].w;
    			return -1;
    		}
    	}
    	int work()
    	{
    		Hash_Table::init();
    		srand(30624700);
    		read(n), read(m);
    		for (int i = 1; i <= n; i++)
    		{
    			w[i].rand();
    			Hash_Table::add(w[i], i);
    		}
    		for (int i = 1; i <= m; i++)
    		{
    			int u, fa, tmp;
    			read(u), read(fa);
    			u ^= lastans, fa ^= lastans;
    			v[i] = (v[fa] ^ w[u]);
    			if (v[i].empty())
    				write(lastans = -1);
    			else if (~(tmp = Hash_Table::query(v[i])))
    				write(lastans = tmp);
    			else if (~(tmp = Hash_Table::query(v[i] ^ v[i])))
    				write(lastans = tmp);
    			else
    				write(lastans = -2);
    			putchar('
    ');
    		}
    		return 0;
    	}
    }
    int main()
    {
    	return zyt::work();
    }
    
  • 相关阅读:
    MySQL server version for the right syntax to use near ‘USING BTREE
    随笔
    [python]自问自答:python -m参数?
    [linux]查看linux下端口占用
    [linux]scp指令
    [编程题目]泥塑课
    How can I learn to program?
    [python]在场景中理解装饰器
    [前端]分享一个Bootstrap可视化布局的网站
    [python]python元类
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/12009458.html
Copyright © 2011-2022 走看看