前言
我意识到小格式与小细节小方法也常常会决定胜败,而且因为这种东西卡住会极大的打击心态,且现在文章没有大致模板的形式不是很好,因此我以后将会令本日学到的小知识点放在前面,然后再写具体的例题和知识点,在记录上还是以知识点为主,如果是单独的题或者说本人没有能力单独提炼出知识点,就把题放在前面。大致是如此格式。
高精随机数
mt19937是在radom包中的一个关键字,是c11中新加入(距离现在都十年多了罢...)的一个特性,利用他生成随机数而不是rand和srand的优点在于高周期于低关联,周期大约可以达到的恐怖数字,用来简单描述已知宇宙暂时是没有太大问题的,而srand是我们前面的老朋友LCG(linear congruential generator)线性同余算法,你可能觉得好像是新的,其实:
不必多言,不懂的可以看前面的rho算法。因此我们不选择使用srand,但为了保证较高的随机性,将srand生成的数字当作mt19937的种子也是常用的做法。srand无法用作蒙特卡洛(Monte Carlo)模拟,加密算法(何止是容易破解,根据鸽笼原理,一个个试都能线性破解掉)
顺便一提,mt19937使用的算法为Mersenne twister算法,而对于硬件玩家,没有超过2kb的空间还是推荐LCG,开支较小并且可迭代。
return 0
在很多编译器中,我们可以省略返回值,但在某些编译器中返回值是不可省略的,如省略会出现段错误,因此请尽量每次都写返回值。
逆元
逆元本来不需要讲解,但经常会搞混,因此放在这里。满足如下条件的数字称为逆元:
称ab在mod m的情况下互为乘法逆元,可以用做除法的mod处理,也即:
可以用三种方法来求逆元:费马小定理,拓展欧几里得算法,线性递推。
可以用洛谷P3811模板练一下手。
本日题目
just a joke
Alice and Bob are playing a game.
At the beginning, there is an undirected graph GGG with nnn nodes.
Alice and Bob take turns to operate, Alice will play first. The player who can't operate will lose the game.
Each turn, the player should do one of the following operations.
1. Select an edge of GGG and delete it from GGG.
2. Select a connected component of GGG which doesn't have any loop, then delete it from GGG.
Alice and Bob are smart enough, you need to find who will win this game.
A connected component of an undirected graph is a set of nodes such that each pair of nodes is connected by a path, and other nodes in the graph are not connected to the nodes in this set.
For example, for graph with 333 nodes and edge set {(1,2),(2,3),(1,3)}.{1,2,3} is a connected component but {1,2},{1,3} are not.
解:注意两个操作都是奇数即可。考试时没有注意到return 0;问题,士气大受打击,不提。确实被开了个玩笑...
Inverse Pair
题目大意:找出所有逆序,求修正逆序需要的步骤数,并利用对应每个
使得C修正逆序所需的步骤数最少。
解法:本题较为巧妙,利用分治lowbit先求得逆序数目,然后将x与x+1这样的合法情况链接,也就是贪心求得可行情况即可。逆序的求得可以参考二分合并的思路,而这里的进一步可以认为是线段树和顺序的思想,也就是说像1这样的数字,更新能力是有限的,并且会被包含入最大的数字形成的计数中,因此不需担心,但是像5这样不在自己位置,却在更前面位置的数字,一定会形成逆序,也就是说计数会更多,这样在后面更小的数字计数时就会显现出来,这就是基本原理。
分治代码:
void change(int k){
for (;k<=n;k+=k&(-k))
t[k]++;
}
int ask(int x){
int s=0;
for (;x;x-=x&(-x)) s+=t[x];
return s;
}
for (int i=1;i<=n;i++)
scanf("%d",&a[i]),p[a[i]]=i;
for (int i=1;i<=n;i++){
ans+=ask(n)-ask(a[i]);
change(a[i]);
}
贪心代码:
int fl=0;
for (int i=2;i<=n;i++)
if (p[i]>p[i-1]) fl=0;
else if (fl==1) fl=0;
else{
ans--;
fl=1;
}
释义:暗含1关系,逆序说明有改进空间。这部分就是链接。
J-Average
利用数字二分的题目,以前遇到过一次,但印象不深刻,本题就选0-1e5范围内二分法来搜索即可。
用逆十字佬的代码来说明一下。
首先是将公式写在纸上,然后能发现是两个平均值的最大值的求和。
接下来进行搜索,50次方即可达到1e16这样的一个效果,题目要求是1e-6的精确度,而最大值为1e5这样,也就是单体最大可以产生1e11的效果,考虑到数组本身也有1e5的大小,实际上50次方是刚刚好够用的。这是数字的选取。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100005;
int n,m,x,y;
double v[N],v2[N];
int a[N],b[N];
double check(double mid,int *a,int n,int x){
for (int i=1;i<=n;i++)
v[i]=v[i-1]+a[i]-mid;
double mn=v[0];
for (int i=x;i<=n;i++){
if (v[i]>=mn) return 1;
mn=min(mn,v[i-x+1]);
}
return 0;
}
double Query(int *a,int n,int x){
double l=0,r=1e5;
for (int T=1;T<=50;T++){
double mid=(l+r)/2;
if (check(mid,a,n,x)) l=mid;
else r=mid;
}
return (l+r)/2;
}
int main(){
scanf("%d%d%d%d",&n,&m,&x,&y);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=m;i++) scanf("%d",&b[i]);
printf("%.10lf\n",Query(a,n,x)+Query(b,m,y));
}
然后查询这里分为了两个部分:一般搜索和求和搜索,分别就是两道简单题:二分法找对应值,寻找最大区间和(大于x的情况。而如果出限制在x内的题的话,思路是一样的,只不过更新最小值时将在对应项前x内进行更新,如果你恰好记下了最小值对应的位置,那么你有可能可以省去不多的一点点资源。
C-LCS
公共子序列
这里提示一点:看到可以不考虑顺序或有大小关系的题目,如果第一眼没有思路,可以考虑排序。
本题就是典型的不需要考虑顺序的题目。因此假设:
易得:相同部分可以用相同字母填充。呃,其实没了,剩下的你用比如z-i什么的不重复的填充一下就行。