随机是好文明

随机是好文明!

小粉红 bot

题意

\(~~~~\) 给出一棵 \(n\) 个结点的树,每个结点有 \([1,10^4]\) 中的颜色中的一种。求最小的连通块大小使得该连通块有 \(k\) 种颜色。
\(~~~~\) \(1\leq n\leq 10^4,1\leq k\leq 5\).

题解

\(~~~~\) 先试想如果颜色的值域是 \([1,k]\) ,那由于 \(k\) 非常小,所以我们有一个 \(\mathcal{O(n3^k)}\) 的状压做法:\(dp_{u,S}\) 表示 \(u\) 结点子树内颜色状态为 \(S\) ,枚举子结点的状态 \(T \subset S\) ,显然转移。

\(~~~~\) 然后现在颜色变多了怎么办?把每个颜色随机赋一个 \([1,k]\) 内的权值做。由于最优答案下一定恰好有 \(k\) 种颜色,对于那 \(k\) 种颜色,我们的合理映射应该是 \(k!=120\) 种,而所有随机总共有 \(k^k=3125\) 种,所以单次正确率为 \(P=\frac{120}{3125}\),那么期望随机次数 \(\frac{1}{P}=26.0417\) ,所以随它个三十多次就可以了。

「CF1285F」Classical?

题意

\(~~~~\)\(n\) 个数 \(\{a_i\}\) 中最大的 \(\operatorname{LCM(}a_i,a_j).\)
\(~~~~\) \(1\leq n,a_i\leq 10^5\)

题解

\(~~~~\) 没有正确性保证,乱随就对了。

\(~~~~\) 取一些数,求出每个数与这些数的 \(\operatorname{LCM}\),记作 \(val_i\),然后把 \(val_i\) 从大到小排序。

\(~~~~\) 对于前面一部分数我们求它们内部的 \(\operatorname{LCM}\) ,对于再前面一部分数我们求它们与所有数的 \(\operatorname{LCM}\) ,最后输出上述过程中产生的最大值。

「CF1168E」 Xor Permutations

题意

\(~~~~\) 构造两个长 \(2^k\)\([0,2^k-1]\) 的排列 \(\{p\},\{q\}\) ,使得 \(p_i \oplus q_i=a_i\).
\(~~~~\) \(2\leq k\leq 12\).

题解

\(~~~~\)\(a_i=p_i\oplus q_i\) 时,所有 \(a_i\) 的异或和应该是 \(2^k-1\) 内每个数出现两次的异或和,那自然为 \(0\) 。若不为 \(0\) 则无解。其余情况由于正解存在构造方案所以必然有解。

\(~~~~\) 当然我们可以把操作改成构造一个 \(p\) 排列,其满足 \(p_i \oplus a_i\) 互不相同。

\(~~~~\) 然后怎么操作呢?每个位置随机一个值去试图放进去,能放进去就直接放,不能就把之前占用了这个值的 \(q\) 那个地方的 \(a_i\) 拉出来重新继续随。

\(~~~~\) 然后就能过???我猜是因为对于一个 \(a_i\) 合法的 \(p_i\) 可能很多所以随便随机一下能过?

「CF1354G」 Find a Gift

题意

\(~~~~\)\(n\) 个盒子,里面有 \(k\) 件装着礼物,其他装着石头。石头的重量均相等,礼物的重量不等但均小于石头。允许 \(50\) 次询问,每次询问可给出两个不交的集合,交互库返回两个集合的重量关系。求编号最小的礼物
\(~~~~ 1\leq n\leq 1000,1\leq k\leq \frac{n}{2}\).

题解

\(~~~~\) 石头是好东西,因为有重量相等的性质。所以假设我们有了一块石头的位置,我们可以干什么?

\(~~~~\) 我们从前往后(注意求答案最小)选取与当前的石头集合相等大小的集合,如果两个集合重量相等说明这个选择的集合也全部都是石头,石头集合的大小 \(\times 2\)。否则必然是因为选择的集合内有礼物,我们就继续往这个集合内求解。由于单次石头集合大小会翻倍,所以最多 \(\lceil \log_2n \rceil=10\) 次就可以求出一个含礼物的集合。

\(~~~~\) 然后呢?然后我们假设选出的含礼物集合大小 \(siz=2^k\)\(k=0\) 直接得到答案,否则我们把集合均分为前后两个部分,拿刚刚求出来的石头集合和它做比较,若前半部分较小就直接保留前半部分(因为求最小编号,即使后面有礼物也不可能成为答案),否则保留后半部分。询问次数仍然是 \(\lceil \log 2 \rceil=10\)

\(~~~~\) 现在的问题是我们没有 miracle,无法凭空获得一块石头,所以我们该怎么获得它?

\(~~~~\) 注意到石头的数量至少是一半,所以我们随机一个位置有至少 \(\frac{1}{2}\) 的概率直接随到石头。而我们还有 \(30\) 次操作可用,那 \(30\) 次操作我们就任选 \(30\) 个位置,依次比较两个位置的大小,每次保留最大的那个作为初始石头。这样随不到石头的概率是 \(\frac{1}{2^{30}}=10^{-9}\),如果真的没随到从某种意义上来说你是欧皇中的欧皇。

「CF1615G」 Maximum Adjacent Pairs

题意

\(~~~~\) 给出 \(n\) 个数,可以把 \(0\) 的数填成 \([1,n]\) 中的数。求最后满足 \(\exist i\in[1,n-1], a_i=a_{i+1}=k\)\(k\) 的个数的最大值。
\(~~~~ 1\leq n\leq 3\times 10^5,0\leq a_i\leq \min(n,600)\).

题解

\(~~~~\) 建图就非常妙的一道题。

\(~~~~\) 考虑先把已经产生了贡献的 \(a_i\) 踢掉,然后来考虑一些 \(0\).

\(~~~~\) 如果有一段连续的长为奇数的 \(0\) 区间 \([l,r]\) ,我们有结论:除了两边都是已经被踢掉的 \(a_i\) ,要么左边与 \(a_{l-1}\) 相同,要么右边与 \(a_{r+1}\) 相同。 因为我们往左或往右匹配一个必然能产生贡献 \(1\) ,同时剩下偶数个可以用任填没有出现过的数产生贡献。但如果我们两边都匹配那我们就丧失了一个用完全没出现过的数创造价值的机会,直观上就不优。当然匹配上就能创造 \(1\) 的价值。

\(~~~~\) 同理,如果有一段连续的长为偶数的 \(0\) 区间 \([l,r]\) ,那:要么两边都与相邻的数相同,要么都不同。 想证明的话方法大概同上。两边都匹配能额外创造 \(1\) 的价值。

\(~~~~\) 然后我们可以把这个问题转化成一个图匹配问题:对于奇数长度区间 \([l,r]\), 建一个点 \(t\)\(t\) 连到 \(a_{l-1}\)\(a_{r+1}\) 的值对应的点,代表两个当中只能连一个。对于偶数长度区间 \([l,r]\),将 \(l\)\(a_{l-1}\) 值对应的点相连,\(r\)\(a_{r+1}\) 值对应的点相连。同时两边都不匹配事实上较都匹配只少一组,所以连接 \(l,r\)

\(~~~~\) 现在就转化成了一个一般图匹配问题。但是我们不会做。带花树?那是啥?一般图匹配对我跟NP有什么区别吗? 而且其实带花树的时间复杂度跑这个也会T。

\(~~~~\) 采用随机化匈牙利算法。匈门!区区带花树怎么能碰瓷匈牙利! 每次跑匈牙利之前先随机打乱 \(u\) 的出边,然后采用匈牙利观察对方是否被占用或者对方被占用的点能否更改匹配对象增广即可。所以为什么是对的呢?

\(~~~~\) 跑出匹配过后我们根据匹配还原出两边的值,然后中间部分直接用完全没有出现过的数填充即可。

查看代码

代码

#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
    T f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
    x*=f;
}
int n,N,vis[600005],Time=0;
int arr[600005],A[600005],buc[600005],Match[600005];
vector <int> G[600005];
vector <int> Free;
void Shuffle(vector <int> &V)
{
    int len=V.size();
    for(int i=0;i<V.size();i++) swap(V[i],V[rand()%len]);
}
bool dfs(int u)
{
    vis[u]=Time; Shuffle(G[u]);
    for(int i=0;i<G[u].size();i++)
    {
        int v=G[u][i];
        if(vis[Match[v]]==Time) continue;
        int x=Match[v];
        Match[u]=v; Match[v]=u; Match[x]=0;
        if(!x||dfs(x)) return true;
        Match[v]=x; Match[x]=v; Match[u]=0;
    }
    return false;
}
inline void AddEdge(int u,int v){G[u].push_back(v),G[v].push_back(u);}
void random_Match()
{
    int Ans=0;Time=3;
    while(clock()/CLOCKS_PER_SEC<=1.5)
        for(int i=1;i<=N;i++) if(!Match[i]) Time++,Ans+=dfs(i);
}
void Print()
{
    for(int i=1;i<=n;i++)
        if(n<Match[i]) arr[i]=Match[i]-n;
    for(int l=1;l<=n;l++)
    {
        int r=l;
        if(!A[l])
        {
            while(!A[r+1]&&r<n) r++;
            if((r-l+1)&1)
            {
                if(arr[l]!=arr[l-1]&&arr[l]) swap(arr[l],arr[r]);
                if(arr[r]!=arr[r+1]&&arr[r]) swap(arr[l],arr[r]);
            }
            l=r;
        }
    }
    for(int l=1;l<=n;l++)
    {
        int r=l;
        if(!arr[l])
        {
            while(!arr[r+1]&&r<n) r++;
            // cerr<<"Fill ["<<l<<","<<r<<"]"<<endl;
            if((r-l+1)&1)
                {for(int i=l;i<r;i+=2) arr[i]=arr[i+1]=Free.back(),Free.pop_back();arr[r]=1;}
            else 
                for(int i=l;i<=r;i+=2) arr[i]=arr[i+1]=Free.back(),Free.pop_back();
            l=r;
        }
    }
    for(int i=1;i<=n;i++) printf("%d ",arr[i]);
}
int main() {
    #ifndef ONLINE_JUDGE
    freopen("input","r",stdin);
    freopen("output","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=n;i++) read(arr[i]),A[i]=arr[i],buc[arr[i]]=1;
    for(int i=1;i<=n;i++) if(!buc[i]) Free.push_back(i);
    N=n+600;
    for(int i=2;i<=n;i++) if(arr[i]==arr[i-1]&&arr[i]) buc[arr[i]]=2;
    for(int l=1;l<=n;l++)
    {
        int r=l;
        if(!arr[l])
        {
            while(!arr[r+1]&&r<n) r++;
            if(!((r-l+1)&1))
            {
                if(arr[l-1]&&buc[arr[l-1]]==1) AddEdge(l,n+arr[l-1]);
                if(arr[r+1]&&buc[arr[r+1]]==1) AddEdge(r,n+arr[r+1]);
                AddEdge(l,r);
            }
            else
            {
                if(arr[l-1]&&buc[arr[l-1]]==1) AddEdge(l,n+arr[l-1]);
                if(arr[r+1]&&buc[arr[r+1]]==1) AddEdge(l,n+arr[r+1]);
            }
            l=r;
        }
    }
    random_Match();Print();
    return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。
*/

「CF1438F」 Olha and Igor

题意

\(~~~~\) 交互题。有一棵高为 \(h\) 的完全二叉树(共 \(2^h-1\) 个点) 。允许 \(n+420\) 次询问,每次询问形如 ? u v w,返回以 \(w\) 为根结点时 \(u,v\)\(\operatorname{LCA}\),求树根。
\(~~~~\) \(3\leq h \leq 18\).

题解

\(~~~~\) 手玩一下,发现你就算乱选 \(u,v,w\) ,大概率返回的会是根结点的两个儿子。

\(~~~~\) 由此我们乱选一些询问,统计每个询问的回答。回答得最多的两个拿来作为 \(rt\) 的两个儿子。

\(~~~~\) 然后枚举其他结点作为根的时候两个儿子的 \(\operatorname{LCA}\) ,显然只有当以原树的根结点为 \(w\) 时会返回 \(w\) ,否则会返回两个儿子的编号。

\(~~~~\) 至于说正确性的证明来复述一下题解的思想:

\(~~~~\) 观察到 ? u v w 事实上返回的是一个到 \(u,v,w\) 的距离和最近的点的编号,并且这个点唯一。假设 \(u\) 结点的三个儿子的子树大小分别是 \(s_1,s_2,s_3\)(没有就视作一个大小为 \(0\) 的子树),那么随机三个相异的数,它作为回答的情况就是 \(s_1\times s_2\times s_3+s_1\times s_2+s_2\times s_3+s_1\times s_3\) (后面加的那堆是因为选到自身也算),总方案数都是 \(\frac{(2^h-1)\times (2^h-2)\times (2^h-3)}{3!}\),那么在 \(h=3\) 的时候两个儿子的概率是 \(\frac{13}{35}=37\%\) ,而在 \(h=18\) 的时候这个概率是 \(\frac{562954248126465}{3002331032584191}=18.75\%\) ,并且都是最大的。因而它们两个的期望也是最大的。

posted @ 2022-10-12 22:22  Azazеl  阅读(41)  评论(0编辑  收藏  举报