XDU_OJ
10月赛
purple place
思路比较简单,从0~99999枚举就可以,但是要注意到每一位的数字表示的是状态,是可以出现前导0的。
所以输入时要用%s读取数据,枚举的数字转成string时注意些细节......
魔法喵点喵
题目比较啰嗦,描述的是dp问题,造个3维数组dp[k][i][j],来表示第1个位置放k,第i个位置放j的最大值,同时预处理好每种j,前面能放的pre[j][]和后面能放的next[j][]就行了
转移方程: dp[k][i][j]=max{dp[k][i-1][t]+w[i][t])}; 其中t是pre[j][]中的元素;由于要满足环状结构,最后一个位置根据倒数第2个和第1个特殊判断
彩云国的经济建设
题意比较难懂,仔细理清楚,其实是个普通的统计问题,选择恰当的数据结构就能轻松解决,输入数据中岛的名字是string,注意处理好。
细胞分裂
最小生成树模型,不知道为神马月赛的时候不停地wa,月赛完一提交就过了- -......,要注意的地方就是,并查集里最后合并节点x,y时,要合并它们的父亲fa[x]=fa[y];
弱弱的军事演习
输入数据很多,充分体现了模块化的重要性。在这里首次意识到到排序算法选择的技巧,普通情况用自带的qsort函数就行了,而这里情况比较特殊,需要多次求前k项的和,而且元素数量很大,所以要用到另一种快速排序。
伪代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 qick_sort(begin,end)
2 {
3 int mid;
4 if(begin>end)return;//递归的边界,很重要
5 mid=part_sort(begin,end);//将元素分为>end和<end的2部分,返回end的位置mid
6 qick_sort(begin,mid-1);
7 qick_sort(mid+1,end);
8 }
这样递归到的区间与1~k无交集时就可以停止排序了,因为我们不关心1~k是否有序。
由于题目的输出是针对原来的序列,所以要保证原序列seq不会受到排序的影响,可以用一个tmp临时存放。
神兽大黄
第一行祭大黄 - -...吼吼~!!
把小白吃的鸡腿当作放在另一个盒子里就行了。然后就可以套用组合数学的‘瓮中之球’公式,c(n-1,2*n-1),答案要求取模;n很大,求组合数时要用到 乘法逆元 这个技巧。
之前不知道,特意查了一下这个知识点,网上不少是错误的;导论上‘线性模运算’一节有提到,要严谨地证明就牵扯到大量抽象代数的知识,这又是另一个领域了.....- -
自己想到一个初等的解法,但适用性可能不那么广。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
求组合数n特别大时,即使边乘边除也会溢出,而取模的运算中 (a/b)%p =(a%p)/(b%p) 不一定成立;怎么办呢?
如果有 (a/b)%p ≡ k%p;
我们只要找到那个k就好了;
这是最自然地想法是写成方程的形式进行代数变换
(a/b) = u + m*p; (u为余数)
为了去掉分母同时乘以bx
ax = u * bx + m*p*bx;
再取模试试
ax mod p = (u * bx) mod p + 0 (第二部分有因子P, 为0了)
这时马上发现 只要 bx mod p = 1 我们就可以得到
ax mod p = (u mod p) * (bx mod p) mod p= u;
ax就是我们要找的那个k ^ ^!
而bx ≡ 1 mod p,所以x就是乘法逆元(这是定义吧?)
(尽量让过程严谨吧...推理有问题的地方希望有人指正- -)
(网上的解答是 (a/b)mod p = a*(1/b) mod p = (a mod p )* (1/b mod p) mod p 其中 1/b mod p 就是求b的乘法逆元,这么说很不严谨,1/b 不为整数,此时模p,很有乱搞的意味- -)
最后 求b的乘法逆元用拓展欧几里得算法。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 int x,y;
2 extculid(int a,int b)
3 {
4 int tmp;
5 if(b==0){x=1,y=0;return}
6 extculid(b,a%b);
7 tmp=x;
8 x=y;
9 y=tmp - a/b * y;
10 }
证明:
ax+by = gcd(a,b); 其中a>b;
b*x'+a%b*y'=gcd(b,a%b);
gcd(a,b) = gcd(b,a%b) (这就是‘拓展欧几里得’名字的来历)
将 a%b= a - a/b * b;代入
x=y';
y=x'- a/b*y'
递归边界是特殊解 b=0时gcd(a,b)=a; ->x=1,y=0;
Let's SPFA 3
无向图求最小圈。要是直接在邻接矩阵里查看对角线的值,必定wa死,因为无向图中1条边就是一个圈了 - -'
正确的办法应该是设 u k v 为最小圈中的3个点k为u->v最短路径上标号最大的点 则路径应该为 dist[u ->v](不经过k点的最短路)+ dist[v->k->u](经过k点的最短路)
怎样求不经过k点的最短路呢?
floyd算法里,迭代n次,每次相当于在u,v之间试图加入i节点,那么只要i在k之前,k就不会被加入u,v的最短路径,可以用一个3维数组来存每次迭代的结果
d[k][i][j]为第k次迭代时i,j的最短距离
那么答案即为 d[0][i][k]+d[0][k][j]+d[k-1][i][j]的最小值(k<n &&k!=i&&k!=j&&i!=j)
到这里可能还存在问题,i,j的最短路径中只考虑了标号为k-1之前的节点,这个答案是否太片面呢?
将圈中的节点,按标号从小到大排序v1,v2.....i,j,k 长度为d[k-1][v1][j]+d[0][j][k]+d[0][k][v1];
因为枚举了k,i,j,所以上述情况不会被遗漏,同一个最小圈会有很多表示方式,只要枚举到了一个就行了。