学习VFK大神推BZOJ,记录一下学习的东西
1004:
burnside:一个置换群的等价计数=(每个置换的置换后等价情况数)/置换总数,每个置换的置换后等价情况数就是置换后没变的数
模意义下的除法:可以使用乘法逆元解,bx mod p=1,x就是b模P的乘法逆元,呢么x≡1/b(mod p),呢么a/b≡ax(mod p),然后用扩展欧几里得求乘法逆元即可(就是解bx mod p=1,某年NOIPT1)
也可以用费马小定理,(a/b)%p=(a/b)%P * b^(p-1)%p=a%p * b^(p-2),然后用快速幂求
1005:
树的prufer编码:(注意这里是无根树)
每次删除树中度数为1且序号最小的节点,并在序列中添加与其相邻的节点的序号,直到树中有两个节点,手玩一组小数据很容易理解(逃
呢么任意一棵树都有唯一的长度为n-2的prufer编码,且度数为m的节点在编码中出现了m-1次
呢么就可以将编码还原回一棵树,从prufer编码的最前端开始扫描节点,设该节点序号为 u ,寻找不在prufer编码的最小序号且没有被标记的节点 v ,连接 u,v,并标记v,将u从prufer编码中删除。扫描下一节点。
这个东西可以用来解决一些给出度数的问题,比如此题
如果高精度除的结果一定是整数的话就没必要写,可以质因子分解,加减一下然后高精度乘
一个小技巧,scanf("%abd");表示输出一个b位的int,如果位数不够前面补a,这个在输出万进制高精度的时候比较好用
再说一遍高精度怎么输出:从la到1输出,如果万进制就输出ans[la],然后从la-1到1输出,补零的个数等于膜的数的零的个数(比如万进制膜10000要补4个零)
1006:
做这题需要好好学一下cdq的弦图与区间图,勉强能理解吧,不懂cdq
首先有一个非常严重的问题,团数并不是团的个数,而是最大团的节点的个数!!!
在弦图中团数=色数,下面是NOIP吧的证明:
首先团数<=色数 应该可以吧...
因为最大团的点必然每一个点的颜色不同...
接下来由于最大势算法求出的 那个 完美消除序列的每一个点都满足它与它后面的相连的点构成一个团,由于这个性质,考虑 节点 i 的时候 ,i后面与它相连的点都必然 是相互连接的, 也就是说 i 后面与它相连的点也是一个团。
考虑那个染色算法...我们染到第i个点的时候,由于上面的性质,与i相连的点必然相互染的是不同的颜色,所以我们考虑色数实际上就是考虑每一个点与它的相连的点构成的团的最大团数即可,所以 团数>=色数。
所以 团数=色数
染色的时候从1开始枚举颜色,直到第i个颜色没有被周围的任何一个节点染色,这个节点的颜色就是i,然后维护最大的i,就是团数
msc求完美染色序列(peo为所求序列):
void mcs(){
for(int i=n;i;i--){
int now=;
for(int j=;j<=n;j++)//注意这个label最大的点并不是和i相邻的点,而是全局点
if(label[j]>=label[now] && !visited[j]) now=j;
visited[now]=true;
peo[i]=now;
for(int j=LINK[now];j;j=e[j].next)
label[e[j].y]++;
}
}
小技巧:通过判断flag[color[e[j].y]]=i来判断i个颜色有没有被周围的任何一个节点染色,这样就不用memset了
1007:
比较简单的给直线求下凸包:
用栈,首先根据斜率排个序,这里建议如果斜率相等呢么y轴上截距递减,这样如果要插入的直线斜率和栈顶斜率相等直接停止就行了
如果要插入的直线和栈中top-1的交点在栈中top和栈中top-2的交点的左边,呢么top--
技巧:fabs用来计算浮点数的绝对值,注意fabs计算的是数的绝对值,所以如果算差不是fabs(a,b)而是fabs(a-b)
1008:
以前水过的题
一些情况下用减法原理转换问题是个不错的选择
相邻两个数不一样的情况数:第一个数有m种可能,第二个数要和它不一样就有m-1种可能,第三个数要和第二个数不一样又有m-1种可能,总数就是m*((m-1)^(n-1))
1009:
比较玄的kmp求矩阵+矩阵快速乘
矩阵快速乘的一个精髓是尝试用两个矩阵乘出状态转移的路线,这个东西比较玄……
kmp的本质是后缀和前缀的匹配,从后面跳到前面,next在求枚举下一位,然后看能匹配到哪里的时候有奇效(解释的很奇怪……具体意思就参考这道题辣)
小技巧:使用a&1来快速判断奇偶
1010:
斜率优化
斜率优化就是在原状态转移方程的基础上对某个阶段i有一个决策k优于决策j,列出不等式,然后经过一系列魔性变化将与i相关的单调东西扔到右边,然后根据左边的斜率维护单调栈,再根据栈中的决策维护f[i]
常见的单调栈维护方法(get_ratio为求斜率):
while(wei<tou && get_ratio(dui[wei],dui[wei+])<=s[i]) wei++;
int temp=dui[wei];
f[i]=f[temp]+fang(s[i]-s[temp]-m);
while(wei<tou && get_ratio(dui[tou],i)<get_ratio(dui[tou-],dui[tou])) tou--;
dui[++tou]=i;
黄学长在证明的时候跳了一步,然后内一步怎么都证不了,心好累qaq
1011:
奇奇怪怪突然出戏的奇葩题
引用chty的一句话:“如何提高智商???这是个永恒的话题”
还是写一下收货,万一哪个丧心病狂的出题人就搞出个类似的题来
如果题目中允许误差,可以观察计算过程中那些东西可以魔性变一下让复杂度降低但依旧在误差允许的范围内
这个东西好玄……真出出来了想出来的几率也很小啊
1012:
难得遇见水题,这回没看人代码,但还是看题解点了一下
如果遇到一个类似可持久化线段树的东西,可以先建一个比较长的线段树,然后暴力buff来增加或删除,可以骗一些分
尤其是只有插入没有删除,或只有删除,甚至是只在队尾或队尾插入或删除的时候这种方法似乎很管用,甚至水过
做题时还是要考虑特殊性而不是一般性
要分析数据量,算一下复杂度看能不能搞一个奇奇怪怪的算法水过
1013:
高斯消元
模板:
void gauss(){
int now=;
for(int i=;i<=n;i++){
int temp=now;
while(temp<=n && !(fabs(a[temp][i])>eps)) temp++;
if(temp>n) continue;
if(temp!=now)
for(int j=;j<=n+;j++)
swap(a[temp][j],a[now][j]);
double c=a[now][i];
for(int j=;j<=n+;j++) a[now][j]/=c;
for(int j=;j<=n;j++)if(j!=now){
c=a[j][i];
for(int k=;k<=n+;k++)
a[j][k]-=c*a[now][k];
}
now++;
}
}
搞消元的过程中已经回带过了,答案直接就是a[i][n+1]
魔性推一推推出n个方程组解n元一次方程,然后用就可以用高斯消元解(然而在考场上推出方程的概率不大)