Loading

模拟62—「Set· Read·题目交流通道·题目难度提升」

Set

考场上结论没弄对,然后爆炸了。
同余意义下差为一个数处理很方便,相等就行了,当和为一个数的时候可以考虑前缀和构造差。
所以这题就直接就出所有前缀和的模完之后的数,一共有 \(n+1\) 个数,所以必定会有相等的 ,找出来输出就行了。

Read

明显发现当出现次数最多的数小于 \(n/2\) 的时候可以构造一种方案全部选择。
所以就是看出现次数最多的数的出现次数是否大于 \(n/2\) , 如果是,出现多少次。

但是本题空间限制很小。

这是 “摩尔投票法”的模版题,可以扩展到求 \(n/3\) 大,空间开销为\(O(1)\)

考场一直想对数列的周期性下手,忘了考虑大于 \(n/2\) 这个事。

\(\%\) 彭师傅考场做法,空间有一定开销,对值域分块,若一个数出现次数大于 \(n/2\) , 他一定在出现次数最多的块中。
最后再扫那个块就行了。

题目交流通道

考场上想的时间不够,感觉没拿到 \(60pts\) 就看题解思考的太少了。
首先考虑边权全部大于0的情况,考虑任意一个三角形 \(a,b,c\)

如果 \(a+b=c\) , 那么 \(c\) 的权可以随便选,有 \(k-c+1\) 种选法。
否则这条边只能选择 \(c\) ,有一种选法 。

在考虑边权可以为0的情况,如果出现类似 \(0+0=0\) 的三角形,就不能简单的选择了,因为并不只有 \(c\) 可以任意选择。
此时把边权为0的点看做一个点,那么新的图每个点都有权,并且点与点之间权不为0,并且边是重边(边权相等)。
先考虑边之间的情况,假设我们要同时满足 \(t\) 条边的限制( \(\rm t=size1*size2\) ),权为 \(ds\)

若存在一个 \(z\) 满足和上面一样的三角形性质,那么这 \(t\) 条边都随便选,方案有 \((k-ds+1)^t\)
否则至少要有一条边权为 \(ds\) , 容斥掉不合法,方案为 \((k-ds+1)^t-(k-ds)^t\)

在考虑一个点(缩完之后的点)之间权为 \(0\) 的方案有多少种。
\(f_i,g_i\)分别代表为 \(0\) 的i个点之间连边的方案数,和任意i个点间连边的方案数。

显然 \(g_i=(k+1)^{\frac{i*(i-1)}{2}}\)

\(f_i\) 的计算发现很多种方法都会算重,所以使用经典的容斥方法。

\[f_i=g_i-\sum_{j=1}^{i-1}f_j*g_{i-j}*C(i-1,j-1)*(k)^{(j)(i-j)} \]

他的方案就等于随便选的方案减去有点不为 \(0\) 的情况,先选出 \(i\) 个点 , 让其内部边为 \(0\) , 让剩下的点随便选。
但是我们保证了连接他们的边不为 \(0\) ,所以肯定是不合法情况。
但是如果随便选择,也会重复计数,因为随便选和固定的中间可能会重复。
所以就相当于固定一个点为基准点,枚举和他边权为 \(0\) 的点集大小,这样可以防止计算重复。

貌似是个挺nb的计数技巧

题目难度提升

比较 \(\rm nb\) 的题,需要大力分类讨论。

首先先来考虑最简单的做法,因为升序排序的序列一定是一种合法情况,并且是字典序最小的情况。
按位确定数字,每次选择他之后排序后面的数字看是否合法就行了。

这个做法对我们并没有任何帮助,所以先来考虑 \(a_i\) 互不相同的情况。
我们可以根据 \(a_i\) 互不相同而发现:每次加入数字,中位数必定会变化,所以中位数只能严格递增。
由此引申出一个结论,一个前缀有解当且仅当未加入的所有数字小于当前中位数。(否则加这个数进去中位数会变小)

那么分当前已经选择的集合中元素个数是奇数还是偶数讨论。

如果是奇数,那么中位数必定落在某个数上,假设是 \(m\) ,假设第一个没放进去且大于中位数的数是 \(k\)
因为要保证 \(k\) 放进去的时候中位数不能变小,所以如果 \((m,k)\) 中间有数,假设最靠近 \(m\) 的数是 \(t\)
那么放进去一个数,中位数一定会变成 \((m+t)/2\) , 并且由于 \(m\leq k,t\leq k\) ,所以中位数不会大于 \(k\),所以可以随便放一个数,肯定放最大的进去。

如果他们中间没数,那么我们放进去一个数 \(p\) , 要保证 \(m+p<=2k\) , 否则 \(k\) 加入的时候会不合法。
\(p=2k-m\) ,那么如果 \((m,p]\) 有数放过了,假设最靠近 \(m\) 的数是 \(z\) , 则 \(z\leq p\) ,那么下次放数中位数一定是 \((z+m)/2\leq k\) , 所以可以随便放。

如果他们中间没数,那么只能放 \((m,p]\) 中最大的数进去了。

再来考虑集合中有偶数个元素。

此时放入元素后中位数会变成一个数,要让他小于等于没放进去的所有数。
假设当前中位数是 \(m\) , 第一个没放进去的大于 \(m\) 的数是 \(k\)
\((m,k)\) 中间有数,则说明下次中位数一定会变成中间的那个数,并且小于 \(k\) , 所以可以随便放。
否则只能放 \(k\) 进去,并且下次中位数一定是 \(k\)

维护中位数可以用对顶堆,维护上述所有操作可以用 set \(\rm splay\)

放一个对顶堆板子,其实是可以胡出来的。

code
priority_queue< int , vector< int > , greater< int > > rheap ; //小根
priority_queue< int , vector< int > , less< int > > lheap ; // 大根

inline void move(){
    while( lheap.size() > rheap.size() + 1 ) rheap.push( lheap.top() ) , lheap.pop() ;
    while( rheap.size() > lheap.size() ) lheap.push( rheap.top() ) , rheap.pop() ;
}

inline double get(){
    if( !lheap.size() && !rheap.size() ) return -Inf ;
    if( ( lheap.size() + rheap.size() ) & 1 ) return lheap.top() ;
    else return ( lheap.top() + rheap.top() ) * 0.5 ;
}

inline void ins( int x ){
    double p = get() ;
    if( x > p ) rheap.push( x ) ;
    else lheap.push( x ) ;
    move() ;
}	

总结

T1结论炸了,前缀和没想到,T2思路错了干老长时间。
像 T2 这种半个小时不沾边就应该换个思路,或者说我傻了因为循环节合并再这题不太现实。
T3,T4想的还是太少了,以后要多项点 T3,T4的特殊性质。
posted @ 2021-09-27 06:37  Soresen  阅读(107)  评论(0)    收藏  举报