ICPC2016大连
A UVALive 7723 Wrestling Match(带权并查集) 325 / 1045
B UVALive 7724 Regular Number(shift-and算法) 34 / 296(赛后过掉)
C UVALive 7725 Game of Taking Stones(java二分) 65 / 328(赛后过掉)
D UVALive 7726 A Simple Math Problem(推公式) 363 / 1339
E UVALive 7727 Aninteresting game(树状数组原理考察) 51 / 196(赛后过掉)
F UVALive 7728 Detachment (推公式) * 242 / 878 *
G UVALive 7729 Garden of Eden(点分治) * 82 / 304*(赛后过掉)
H UVALive 7730 To begin or not to begin(签到) 487 / 739
I UVALive 7731 Convex(签到)**483 / 653**
J UVALive 7732 Find Small A(签到) 479 / 952
K UVALive 7733 Guess the number 31 / 117
题解:
HIJ:sb题(J题注意2^8=256 =_=||)
A:带权并查集
因为本题目只有两种状态,不是好就是坏。所以总的状态就只有0,1两种。
find函数回溯的时候sum[x]=(sum[x]+sum[t])%2,t是x的初始祖先节点;
在加入并查集时,fx=find_(x),fy=find_(y),当fx!=fy时sum[fy]=(sum[x]-sum[y]+1+2)%2;
当相同时需要判断是否符合sum[x]!=sum[y],不符合说明有阵营不明确的人
另外本题还有一个注意点,有些并查集只有自己一个节点,这种情况下也是
不明确的人,所以需要用cnt数组记录每个并查集中节点数目。
最后一个注意点,有可能是加入并查集没有错误,但是最后可能一个并查集中
有些点在好的阵营中,另外在坏的阵营中,需要将这种情况排除掉
https://paste.ubuntu.com/p/rBtqr2JWD7/
G:点分治算法
问你有多少对(u,v),使得在此路径上的种类数有k个
点分治就是基于树上的节点进行分治。
点分治的本质其实是将一棵树拆分成许多棵子树处理,并不断进行。
这其中有两个部分是不变的,一个是计算重心,一个是对子树进行路径的计算或者记录状态
剩下的就是在总的dfs下,计算通过当前根节点的路径上的信息(一般计数题目是总体计算然后剪去合并的冲突项,有些问满足条件的最长路径是遍历过程中不断寻找最大值)
本题目一个注意点就是在计算当前子树的满足条件的路径数时,为了降低复杂度
用下面这种方式,其实s0=(s0-1)&sta[i],这个操作可以实现对sta[i]的1的遍历
比如与10010或等于11111的最小的数时1101,然后是1111,之后是11101,最后是11111
实际模拟一遍就知道怎么回事了
https://paste.ubuntu.com/p/2dxdYph2zX/
for(int s0=sta[i];s0;s0=(s0-1)&sta[i]) res+=cou[((1<<k)-1)^s0];
D:推公式
题目给出两个等式:
X+Y=A
X
+
Y
=
A
LCM(X,Y)=B
L
C
M
(
X
,
Y
)
=
B
我们可以看出A,B的GCD也就是X,Y的GCD
我们首先让两个等式同时除以
GCD(A,B)
G
C
D
(
A
,
B
)
便得到:
X1+Y1=A/GCD(A,B)
X
1
+
Y
1
=
A
/
G
C
D
(
A
,
B
)
X1∗Y1=B/GCD(A,B)
X
1
∗
Y
1
=
B
/
G
C
D
(
A
,
B
)
X1∗GCD(A,B)=X,Y1∗GCD(A,B)=Y
X
1
∗
G
C
D
(
A
,
B
)
=
X
,
Y
1
∗
G
C
D
(
A
,
B
)
=
Y
解方程求出X1,Y1,乘以GCD(A,B)即可
https://paste.ubuntu.com/p/3CJP22CBt8/
C.高精度
威佐夫博弈只要满足
所以只要大数+java二分高精度求出 5–√ 5 就可以了。
https://paste.ubuntu.com/p/twsbyFXPJT/
E.树状数组
首先我们要深刻理解树状数组的内部原理
我们先来看一下每个数的二进制码和它们对应的lowbit
![树状数组原理](https://i-blog.csdnimg.cn/blog_migrate/1eb150783fff9d0ff6e43402cbeccb99.png)
1.lowbit(i)表示i能整除的最大的2的幂次
2.i-lowbit(i)表示跳到前一个整幂次的二进制区间
3.i+lowbit(i)表示跳到下一个包含当前区间的区间
如果知道这三个性质,
我们先来看第一问,求某两段之间的lowbit之和,
我们首先求出 sum(1,i−1) s u m ( 1 , i − 1 ) 再求出 sum(1,j) s u m ( 1 , j ) ,前缀和相减就可以了
现在问题变为如何求出 sum(1−n) s u m ( 1 − n ) ,我们来看性质1,我们发现我们只要知道
有多少恰好只能整除1的
有多少恰好只能整除2的
有多少恰好只能整除4的….
我们可以发现恰好整除1个数为 [n/1−n/2] [ n / 1 − n / 2 ] 向下取整
因为能够整除1的只包含恰好整除1的和能够整除2的
以此类推,我们就可以o(1)的计算出所有2的幂次的贡献,之后就扫一遍算贡献就可以了
单次复杂度是log的
再来看第二问,问某个数被加过几次,想想树状数组的add函数,这个问题就不难想了,每个数字只会在包含他的区间被重新添加,我们只要观察性质三,就很容易算出第二问了!于是整个题就解决了。
https://paste.ubuntu.com/p/dTQsG8mfNh/
F.推公式
本题题意就是让一个数拆成多个数之和,而且这些数的乘积最大,我们知道当我们拆的数越多,乘积就越大,然题意要求每个数不重复,所以我们要让这些数尽量靠近,分下面两种情况
根据以上两种情况我们列出等式
https://paste.ubuntu.com/p/PbSbmttPMK/
B.Shift-And算法
l如果你学过这个算法的话,那么就是模板题。
Shift-And算法解决的就是每个位置可以放很多种字符时的字符串匹配问题
ans数组表示的是有哪些长度即是目标串后缀又是模式串前缀
求模式串在目标串中出现次数的时候
例如11001就代表当前串有长度为1,4,5的串既是目标串后缀又是模式串前缀
所以如果我们想统计模式串出现次数,就是ans[len]=1时答案++;
所以这道题就可以用Shift-And算法结束了!
https://paste.ubuntu.com/p/5S6RNvSvYG/