连飞学长的爱 解题报告
2019/2/22
A-A
CodeForces - 560A
题意:
有一套纸币,有n个币种,问是否能构成任意面值?
input:
n
a1 a2 … an
数据范围:
n<=1000 ai<=1e6
思路:
只要判断币种面值有没有1即可
B-B
CodeForces - 560B
题意:
有两张矩形的画,和一个矩形的墙,问墙上是否能放下两张画?
input:
a1 b1(墙的长、宽)
a2 b2(第一张画的长、宽)
a3 b3(第二张华的长、宽)
数据范围:
ai,bi<=1000
思路:
四种拼接方式,一种满足即可
bool solve()
{
int x = a2+a3;
int y = max(b2,b3);
if(x<=a1&&y<=b1||x<=b1&&y<=a1) return true;
x = a2+b3;
y = max(b2,a3);
if(x<=a1&&y<=b1||x<=b1&&y<=a1) return true;
x = b2+a3;
y = max(a2,b3);
if(x<=a1&&y<=b1||x<=b1&&y<=a1) return true;
x = b2+b3;
y = max(a2,a3);
if(x<=a1&&y<=b1||x<=b1&&y<=a1) return true;
return false;
}
C-C
CodeForces - 559B
题意:
两个字符串满足下列两个条件之一输出yes:
(1) A[1,2…lena] == B[1,2…lenb]
(2) 把A分成前后长度相等的两部分a1+a2 B分成b1+b2,满足
(a1=b1且a2=b2) 或 (a1=b2且a2=b1)
输入:
(字符串)A
(字符串)B
数据范围
length <= 200 000
思路:
递归判断即可
拿到两个字符串A,B
- 先判断A,B是否相等 若相等,return true;
- else if len为奇数,return false;
- else return (a1=b1且a2=b2) 或 (a1=b2且a2=b1)
- 这里可以剪枝:如果字符串A,B中相同字母出现的次数不同,直接return false;
我的代码<-戳这里
D-D
CodeForces - 559C
题意: 小学奥数题升级版->走网格
网各为h*w (h,w<=1e5)
有n个格子是黑色的,其余是白色的(n<=2000)
从左上角走到右下角,每次只能向下或向右走1格(不能走到格子外面)
其中黑色的格子不能走,问从左上角到右下角的方案数,答案对1e9+7取模
数据范围:
1<=h,w<=1e5
1<=n<=2000
mod = 1000 000 007
思路:
-
这道题,小学奥数的做法是一行一行的递推
因为只能向下走或向右走,所以从起始走到格子(i,j)的方案数dp[i][j] = dp[i-1][j] + dp[i][j-1] (dp[1][1]==1)
遇到黑色的格子,直接dp[i][j] = 0; -
如果没有黑色的格子,从起始位置(1,1)走到格子(i,j)的方案数就是C(i+j-2,i)。因为从(1,1)到(i,j)总距离为垂直距离i-1加上水平距离j-1,令tot = i-1+j-1,只是在tot步中选择i-1步往下走
C(n,m)代表组合数 -
为了叙述方便我们先写一个函数Dis(x0,y0,x1,y1)
其中x1>=x0, y1>=y0 这个函数表示从点(x0,y0)走到点(x1,y1)的方案数
则Dis(x0,y0,x1,y1) = C(x1-x0+y0-y1,x1-x0) -
因为黑色格子的个数n比较少,我们可以从黑色格子入手
如果一个格子(i,j)的左上方没有其它的黑色格子,则起始点到它的走法为Dis(1,1,i,j)
如果一个格子(i,j)的左上方有黑色格子(a,b)那么不能由P走到当前的格子,所以要减去(a,b)到(i,j)的方案数
假设起始格子到(a,b)的方案数dp(a,b)已知,则要减去的数为:dp(a,b) * Dis(a,b,i,j) -
所以,从起始格子走到右下角的方案数就是Dis(1,1,h,w)减去所有在它左上方的黑色格子(a,b)对它的影响:dp(a,b)*dis(a,b,h,w) 而计算起点到黑色格子(a,b)的方案数又要用(a,b)左上方的黑色格子的方案
所以把所有的黑色格子按先x后y排序,一次求出dp(xi,yi)即可
这里我们可以O(n)预处理出2e5以内的所有阶乘和阶乘的逆元
int N = 2e5;
int mod = 1e9+7;
long long Fac[N+10],rev[N+10];
void init()
{
Fac[0] = 1;
for(int i=2; i<=N; ++i)
Fac[i] = Fac[i-1]*i%mod;
//对质数取模,逆元Rev(x) = x^(mod-2)
rev[N] = fast_pow(Fac[N],mod-2);
for(int i=N-1; i>=1; --i)
rev[i] = rev[i+1]*i%mod;//逆元的性质
}
long long C(long long n,long long m)
{
if(!m||m==n) return 1ll;
return Fac[n]*rev[m]%mod*rev[n-m]%mod;
}
E-E
CodeForces - 558C
题意:
给n个正整数(n<=100000),a1,a2…an(1<=ai<=100000)
每操作一次,可以把任意一个数乘以2或者除以2(整数除法)
问最少操作多少次可以使这n个数都相同?
输入:
n
a1 a2… an
数据范围:
1<=n<=100 000
1<=ai<=100 000
思路:
用二进制模拟
- 先把所有的数都写成二进制的形式按高位对齐,每一位求n个数的和,若cnt[i]==n说明n个数的这一位都是1,简记为c[i]
- 一个数乘以2相当于在它后面加了一个0,除以2相当于删去了最后一位(0或1)
高位 -> 低位
3: 1 1
5: 1 0 1
6: 1 1 0
c: 3 2 1 - 我一开始的思路是这样的:
从最高位开始,
如果c[i]==0说明n个数该为全是0或者有的数没有这么多位,可以通过乘以2添加0;
如果c[i]==n,说明这n个数该为均为1;如果0<cnt[i]<n 则有的为1,有的为0,因为无法凭空产生1,所以i前面的相等的数就是最后每个ai操作后的结果
找到第一个0<cnt[i]<n的位数,己它前面有x位,所以结果为 sum(abs(bitof(a[i]-x)) i=1,2…n - 然后就Wa了
- 其实还有一种情况,虽然我们不能凭空添加1,但是我们可以删去1和添加0
看一个死亡样例:(下面6个数是二进制形式,高位对齐)
101100 0000
101100 0000
101100 0000
101100 0000
101101
1011
显然,按照刚才的思路,只有前5位cnt[i]为0或n
那么如过ai最后可以都变成10110或者1011或者101或者10或者1你应该懂我意思吧
就是说所取前1,2,3,4,5位都可以
取一个操作最少的就是答案
ans = |10-5|*4 + |6-5|+|4-5| = 22
但是其实,我们可以把第5第6个数都变为101100 0000
第5个数除一次,乘5次,第6个数乘6次
ans = 5+1+6 = 12显然比22小
这是因为: 刚才的思路碰到多余的1就直接投降了,其实我们可以吧多余的1删掉,甚至还可以删掉1以后在后面添很多0
最后a[i]变为的相等的数可能位数比x要多,设为y
当然,二进制形式的x位之后全部是0
我的代码
F-F
CodeForces - 558D
题意:
有一棵满二叉树,高度为h(1<=h<=50),顶点为序号1,顶点为第1层
叶子节点都在第h层,其中,有一个出口在某个叶子节点上
然后有q(q<=1e5)个询问,每个询问有4个数cent L R ans
问在第cent层序号范围在[L,R]的节点的是否有出口的祖先?(自己也是自己的祖先) ans=1表示有,ans=0表示没有
输入:
h q
cent1 L1 R1 ans1
cent2 L2 R2 ans2
…
centq Lq Rq ansq
输出
若给的数据自相矛盾输出“cheated…”
若能判断出出口所在叶子节点的序号,输出序号
否则输出“data is not suffient”
数据范围:
1<=h<=50
0<=q<=1e5
思路:
离线处理,给定一组L,R,可以求出其在第h层的所有子节点[l,r]
把ans=1的放一起,ans=0的放一起,按区间左端点排序
- 若h=1则出口一定在1,稍加判断即可
- 若只有ans = 1的询问,则若有两个区间完全不相交,说明两有多个出口,矛盾; 若最后求得所有区间交集为0,也矛盾;若交集长度>1则数据不充足;若区间长度为1,输出这个r
- 若只有ans = 0的区间,则存在多个区间,是没有出口的,若若叶子节点的全集减去这些区间的交集所得的差集为0,说明没出口,矛盾; 若差集长度只有一个长度为1的区间,则是答案;若差集有多个区间,则条件不充足
- 若ans=0和ans=1均存在 先求出全集和ans=0差集,然后求ans=1的集合时,若有两个不想交的ans=1的集合,判断是否被否定
若存在两个不相交的集合多没有被ans=0的询问否定则矛盾:
否则看剩下的区间若长度为1,输出答案;
若没有区间,则也矛盾;若只剩一个长度大于1的区间,则条件不充足
我的代码
(代码看似长,其实后面的case 3都是复制的case 1和2)
G-G
CodeForces - 527C
题意:
有一块玻璃,h*w(1<=h,w<=2e5)划线n次,每次在玻璃上垂直或水平画一条线,问每次画完线之后最大的矩形面积
输入:
h w n
ch1 x1
ch2 x2
…
chq xq
若ch = V表示垂直划
若ch = H表示水平画线
数据范围:
2<=h,w<=2e5
1<=n<=2e5
思路:
矩形面积最大,一定是长和宽最大
用set存线,用map存线之间的间距(很多人用mulitset都差不多)
每次找到离新画的线最近的两条线,得到x1,x2
map[x2-x1]–; map[x2-x]++; map[x-x1]++;
更新map值即可,若键对应的值为0,删除该节点
map的最大值为 *( --mp.end() )
(据说这道题正解是并查集?)
我的代码
H-H
CodeForces - 527D
题意:
N-P完全问题时间复杂度很难达到多项式的时间复杂度,如在n个点求一个最大的完全图的节点数
本题对于节点之间边的定义有所不同。
有n个点(1<=n<=2e5),所有点都在X轴上,每个点有坐标xi和重量wi,若两个点距离>=重量之和,则两个点之间有无向边
求这样的完全图的最大节点数
输入:
n
x1 w1
x2 w2
…
xn wn
数据范围:
1<=n<=2e5
0<=xi<=1e9
1<=wi<=1e9
思路:
分析见代码的注释部分
分析过后可以转化为经典的贪心问题:
最多可以完整的观看几个电视节目?
I-I
CodeForces - 526C
题意:
意思大致是有两种糖果,每种无限多,一个爱吃糖果的小怪物最多吃capacity克的糖果,每种糖果一棵的重量和带来的开心度分别为wi和ei
求最大的开心度
输入:
capacity
w1 e1
w2 e2
数据范围:
均为[1,1e9]
思路:
有人说用完全背包做
我是直接w1取x个0-1e7个,暴力更新答案,然后w2再搞一遍
[我的代码(https://paste.ubuntu.com/p/BSMv9JK8Df/)
J-J
CodeForces - 526D
题意:
大致是说给一个长度为n的字符串S,为每个前缀是否可以看成是K+1个A和K个B交错组成的如下形式的字符串
ABABA…A (K+1个A,K个B, 其中A或B可以是空串)
输入:
n K
S
输出
长度为n的字符串,若长度为i的前缀可以看成ABA…A(K个B)这种形式,就在第i位输出1,否则输出0
数据范围:
1<=n,k<=1000 000
思路:
由于所求的串是循环的,所以我们需要知道每个前缀的循环节
可以用kmp算法中的next数组求得
kmp求最小循环节
定理:若长度为len的字符串S存在最小循环节,则循环节的长度L = len - next[len]
循环节为S的子串[S0,S1,S2…S(L-1)]
看一个例子:
15 2
abcabcabcabctql
1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 (index)
a b c a b c a b c a b c t q l (S)
0 0 0 1 2 3 4 5 6 7 8 9 0 0 0 (Next)
1 1 1 1 1 2 2 2 3 3 3 4 1 1 1 (x)
循环节的长度为L,则循环次数x = len/L(向下取整)
现在有三种情况:
- case 1:B串为空串,则需要出现K+1个完整的A
所以这时,只要保证len%L=0且x%(k+1)=0即可 - case 2:A串为空串,则需要出现K个完整的B
与上一种情况同理,len%L==0 且 x%K=0 - case 3: A串和B串都不为空
若(最小的)循环节为D,则
当len%L=0时:原串可以表示为DDD…D(x个D)
当len%L!=0时,原串可以表示位DDD…Dd(其中有x个D,d为D长度为len%L的前缀)
len%L!=0的情况比较一般,因为我们可以吧D也看作是d
设D = d(前缀) + p(除去前缀的后缀)
设AB = C
则A可以写成DD…D(D的个数可能为0)的形式,B可以写成pDD…D(D的个数可能为0)的形式
CCC…CA(K个C)
C.length = len/L
A.length = len%L
由于C.length > A.length,则一个充分条件就是len/K > len%L
只要满足了这个条件,那么就可以把长度为L的前缀看成C(A+B),把长度为len%L的前缀看成A
我能不能说除了这种方法构造不出来ABABA的串,所以这也是必要条件
K_K
CodeForces - 535C
题意:
题意有点儿不好读懂
给一个无限长的首项为A公差为B的等差数列
有多个询问,每次给L m t
表示最多可以操作t次,每次最多可以使m个数减一,
问从L开始,最多可以使多长的区间都减为零?
输入:
A B n
一下为n组询问:
l t m
数据范围:
A,B <= 1e6
n<=1e5
思路:
二分:由于最多操作t次,所以区间右边端点<=t
区间和小于等于t*m
这道题让我知道即使开了取消同步cin,cout还是妥妥的TLE
L-L
CodeForces - 535D
题意:
给一个字符串P,要构造一个长度为n的字符串S,这个字符串要满足匹配子串P至少m次,给出m个匹配的位置posi,表示构造出的字符串P的第posi位到第posi+p.length-1位的子串必须和p完全一样,求能构造出多少种满足条件的字符串(答案对1e9+7取模)
输入:
n m
pos1 pos2 … posm
数据范围:
1 ≤ n ≤ 1000 000
0 ≤ m ≤ n-|p|+1
思路:
照题意模拟。pos从小到大排序,若两个pos之差>=lengthOf§则一定有解,若大于,则未涉及的位置可以任意取26个字母,cnt += posj-posi
两个串有重叠,则要满足题意,相交的部分要为模式串的前缀和后缀的公共部分
预处理出所有前缀和后缀相同的公共部分,用KMP的Next数组可以得到模式串前后缀最长的公共部分
存在多个相同的前后缀,则一定有循环:
p[1,2] == p[19,20] 且 p[1,2,3,4] == p[17,18,19,20]
=>p[1,2]==p[3,4]==p[19,20]==p[17,18]
可以预处理出字符串P所有可以和前缀匹配上的后缀的长度(有点儿抽象,看下面这个例子)
例如abcabcababcczzzzzabcabcabc
其中所有满足条件的后缀的长度就是3,6,9
然后按题意模拟,如果重叠了且重叠的后缀长度和前缀无法匹配,则直接输出0,return 0;
若不冲突,则输出26^cnt
ps: 第一次markdown,还是挺好用的
从2月22号早上开始写,火车下铺写了一下午,写到电脑没电,然后今天晚上终于写完了。