李聪的博客

少年易老学难成,一寸光阴不可轻


  • 首页

  • 标签

  • 分类

  • 归档

错误总结

发表于 2018-04-18 | 分类于 ACM

每天看一遍

  • 留意特殊情况,例如推出的式子分母项为0(等比数列$q$为1)
  • 图论问题邻接表的大小,有时边数远大于点数,或者对每条边需要插入正反两条边,需要看清楚开多大的邻接表。
  • 一个题目中不同数组可能需要不同的大小,不要弄混。
  • 编码时牢记每个变量的意义,不要用混。
  • 位与或的优先级只比逻辑与或高1,比大于小于等于要低,使用时务必加上括号。
  • 超过int范围的移位运算需要使用1LL<<
  • 移位运算优先级比加减乘除低
  • 永远考虑0,1等特殊情况,对每个值可能为0,1的变量都要加以考虑。
  • 时间吃紧的题目,看到多组数据一定要尽可能节省的初始化,BIT可以更改update上限,尤其是在ZOJ上。
  • 内存吃紧时把ll改回int
  • 对于某些存疑的结论,要考虑在可接受的复杂度增加情况下放弃,结论可能是错的。
  • 手动生成的字符串给末尾加上\0

欧拉函数的理解性证明

发表于 2018-04-14 | 分类于 ACM

定义

$\varphi(n)$:$1$到$n$中与$n$互质的数的个数
$$
\varphi(n)=n\prod_{p|n}(1-{1\over p})
$$
其中$p$是$n$的素因子

证明

关于这个公式的理解,可以先考虑只有一个素数因子的形式。
若$n=a^p$,则$[1,n]$中能被a整除的数分别为$a,2a,3a,…,a^{p-1}a$,共$a^{p-1}={n\over a}$个,剩下的是不能被$a$整除的数,自然的就和n互质。因此容易得到$\varphi(n)=n−{n\over a}=n(1−{1\over a})$
从上面的例子可以看出,a的倍数其实是在$[1,x]$之间平均分布的。这样的平均分布适用于任何的数的倍数。

当一个数有多个素数因子的时候,可以利用这些因子的倍数是各自平均分布的性质,依次累乘上$(1−{1\over p})$.相当于筛去了$p$的倍数。
举个例子:$30=2·3·5$
$1,2,3,4,5,6,7,8,9,10,……,28,29,30$
共30个数,先乘$(1−{1\over 2})$剩下了15个数:
$1,3,5,7,9,11,13,15,17,19,21,23,25,27,29$
再乘$(1−{1\over 3})$筛去了5个,剩下10个数:
1,5,7,11,13,17,19,23,25,29
再乘$(1−{1\over 5})$筛去2个,剩下8个数:
1,7,11,13,17,19,23,29
这是因为被筛去的数是平均分布的,则剩下的数中,为另一个素数的倍数也是平均分布的。

HDU-6266

发表于 2018-04-14 | 分类于 ACM

总结

要合理分析题目难度,不要高估,检查时要检查所有情况,不要只盯着一个地方。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "stdio.h"
#include "cstring"
#include "stdlib.h"
#include "map"
#include "string"
#include "iostream"
#include "set"
#include "queue"
#include "vector"
#define FOP freopen("input.txt","r",stdin)
#define Met(x,y) memset(x,y,sizeof x)
#define L index*2
#define R index*2+1
using namespace std;
typedef long long ll;
const ll MAXN=1e6+100;
const ll MOD=1e9+7;
ll a[MAXN];
int main(int argc, char const *argv[]) {
// cout<<188*0.6<<endl;
ll T_T;
cin>>T_T;
while(T_T--)
{
ll n,d;
cin>>n>>d;
bool a1=1;
ll n1=0;
for(int i=0;i<n;++i)
{
scanf("%lld",&a[i]);
if(a[i]==1)
n1++;
// printf("n1==%lld\n",n1);
}
if (d==1) {
if (n1==n && n%3==0) {
puts("No");
} else {
puts("Yes");
}
} else {
if((n1==n-1 && n%3==0) || (n1==n && (n-1)%3==0) ||(n1==n-1 && (n-1)%3==0))
puts("No");
else puts("Yes");
}
}
}

Codeforces-961E

发表于 2018-04-13 | 分类于 ACM

题解

一般自己能独立做出的题目是不写题解的,但是这是我第一道独立做出的自己总结的 树状数组解决两个不等式限制问题 ,因此写篇题解纪念一下
设输入数据为$a_1····a_n$,若一个$(i,j)$对(设$i<j$)满足题目条件,则需满足三个条件
$$
\begin{cases}
i<j\\a[j]\ge i\\a[i]\ge j
\end{cases}
\implies
\begin{cases}
j\le min(a[i],i-1) \\ a[j]\ge i
\end{cases}
$$
化简后变为两个条件
从1到n循环
每轮循环更新update(i,1),这样树状数组中每个为1的点都代表一个$j$
同时de[a[i]].push_back(i),这样当$i$递增到$a[i]$时,下轮循环$i$递增不再满足$a[j]\ge i$,此时de[i]中有应该删除的$j$点,对这些点update(j,-1)即可保证树状数组中的点都满足第二个条件。
再通过sum(min(a[i],i-1))完成对第一个条件的限制,可求得同时满足两个条件的$j$点的个数,问题得解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=2e5+100;

ll BIT[MAXN];
ll Lowbit(ll x) // 2^k
{
return x&(-x);
}
void update(ll i, ll x)//i点增量为x
{
while(i <MAXN)
{
BIT[i] += x;
i += Lowbit(i);
}
}
ll sum(ll x)//区间求和 [1,x]
{
ll sum=0;
while(x>0)
{
sum+=BIT[x];
x-=Lowbit(x);
}
return sum;
}
std::vector<int> de[MAXN];
ll a[MAXN],show[MAXN];
int main(int argc, char const *argv[]) {
// freopen("input.txt","r",stdin);
ll n;
ll ans=0;
ll up=2e5;
std::cin >> n;
memset(BIT,0,sizeof BIT);
memset(show,0,sizeof show);
for(int i=1;i<=n;++i)
{
scanf("%lld",&a[i]);
if(a[i]>i)update(i,1);
show[i]++;
if(i>1)
ans+=sum(min(i-1LL,a[i]));
if(a[i]>i && a[i]<MAXN)de[a[i]].push_back(i);
for(ll v:de[i])
update(v,-1),show[v]--;
}
cout<<ans<<endl;
return 0;
}

其他总结的“树状数组满足两个不等式限制问题”的题解

ZOJ 4008 Yet Another Tree Query Problem
HDU-6230 HDU-5542 利用树状数组解决两个不等式限制的问题

Codeforces-961D

发表于 2018-04-12 | 分类于 ACM

题意

给出一些点,判断这些点能否都在小于等于两条直线上

思路

首先找出三个不在一条直线上的点,可以得到三条直线。
依次枚举每条直线可解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <cstdio>
#include "stack"
#include "list"
#include <cstring>
#include <algorithm>
#include "iostream"
#include <cmath>
#include <vector>
#include "set"
#include "queue"
using namespace std;
#define FOP freopen("input.txt","r",stdin)
#define R index*2+1
#define Met(x,y) memset(x,y,sizeof x)
#define L index*2
typedef long long ll;
const ll MAXN =3e5+100;
const ll INF = 1e18;
const ll MOD = 1e9+7;

struct Point{
ll x,y;
bool exist;
void read()
{
scanf("%lld%lld",&x,&y);
}
Point(double x_=0,double y_=0)
{
x=x_,y=y_;
}
Point operator -(const Point& rhs)
{
return Point(x-rhs.x,y-rhs.y);
}
};
struct Line{
ll A,B,C;
Line(Point a,Point b)
{
A=b.y-a.y;
B=a.x-b.x;
C=b.x*a.y-a.x*b.y;
}
Line(){}
};
Point p[MAXN];
bool PointOnLine(Point a,Line b)
{
return b.A*a.x+b.B*a.y+b.C==0;
}
int main(int argc, char const *argv[]) {
// freopen("input.txt","r",stdin);
ll n;
cin>>n;
for(int i=0;i<n;++i)
p[i].read();
if(n<=4)
{
puts("YES");
return 0;
}
Line l[2];
ll pp[3];
pp[0]=0; pp[1]=1;
Line tem(p[0],p[1]);
bool flag=1;
for(int i=2;i<n;++i)
if (!PointOnLine(p[i],tem)) {
pp[2]=i;
flag=0;
}
if (flag) {
puts("YES");
return 0;
}
for(int i=0;i<3;++i)
{
l[0]=Line(p[pp[i]],p[pp[(i+1)%3]]);
bool yes=1,al=0;
for(int j=0;j<n;++j)
if (al) {
if(!PointOnLine(p[j],l[0]) && !PointOnLine(p[j],l[1]))
{
yes=0;
break;
}
} else {
if(i==2 && j==1)
i=i;
if(PointOnLine(p[j],l[0]) || j==pp[(i+2)%3]) continue;
al=1;
l[1]=Line(p[j],p[pp[(i+2)%3]]);
}
if (yes) {
puts("YES");
return 0;
}
}
puts("NO");
return 0;
}

第13届景驰-埃森哲杯广东工业大学ACM程序设计大赛 C-平分游戏

发表于 2018-04-06 | 分类于 ACM

题解

首先应该想到,同学们会被划分成$gcd(n,k+1)$个集合,同一个集合内的同学之间才能互相交换硬币。对每个集合,问题被化简成如下情况。
$\require{AMScd}$
$$
\begin{CD}
a_0 @>x_0>> a_1\\
@A x_3 A A @VV x_1 V\\
a_3 @<<x_2< a_2
\end{CD}
$$
其中$x$表示按箭头方向传递硬币的数量,为负代表按与箭头相反的方向传递硬币。
环状方程问题应想到当确定一个变量时,其他所有变量都可得出
设$x_0$为常量,$avg$为每个人最后应拥有的硬币
得出方程
$$
a_1+x_0-x_1=avg \\ x_1=-avg+a_1+x_0
$$
$$
a_2+x_1-x_2=avg
$$
带入$x_1$
$$
x_2=-2avg+a_1+a_2+x_0
$$
以此类推得到公式
$$x_i=-i*avg+\sum_{j=1}^i a_j+x_0 $$

答案需要求总移动次数 $\sum_{i=0}^{n-1}|x_i|$
而

$$
|x_i|=|i*avg-\sum_{j=1}^i a_i-x_0|$$

相当于在数轴上求一点 $x_0$ 使其到各点 $i*avg-\sum_{j=1}^ia_i$ 的距离之和最小
排序之后求中位数即可

Codeforces-940E

发表于 2018-03-29 | 分类于 ACM

题解

$dp[i]$表示前i个数字组成的最小答案,得到状态转移方程
$$ dp[i]={min(\\
dp[i-1]+a[i],\\
dp[i-c]+\sum_{j=i-c+1}^{i}a[i]-\min_{j=i-c+1}^{i}a[i]
\\) }
$$
维护动态集合(有不停的添加删除元素)的最小值用muiltset

总结

解题时要想到枚举思路,dp,二分,贪心等常用思路都要考虑到。当确定$dp[i]$的意义后,要大胆的写出方程。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include "bits/stdc++.h"
#define FOP freopen("input.txt","r",stdin)
#define Met(x,y) memset(x,y,sizeof x)
#define L index*2
#define R index*2+1
using namespace std;
typedef long long ll;
const ll MAXN=1e6+100;
const ll MOD=1e9+7;
ll arr[MAXN];
ll dp[MAXN];
ll sum[MAXN];
multiset<ll> se;
int main(int argc, char const *argv[]) {
ll n,c;
while (cin>>n>>c) {
se.clear();
for(int i=1;i<=n;++i)
scanf("%lld",&arr[i]);
sum[0]=0;
for(int i=1;i<=n;++i)
sum[i]=sum[i-1]+arr[i];
dp[0]=0;
for(int i=1;i<c;++i)
{
dp[i]=dp[i-1]+arr[i];
se.insert(arr[i]);
}
for(int i=c;i<=n;++i)
{
se.insert(arr[i]);
dp[i]=min(dp[i-1]+arr[i],dp[i-c]+sum[i]-sum[i-c]-*(se.begin()));
se.erase(se.find(arr[i-c+1]));
}
cout<<dp[n]<<endl;
}
return 0;
}

HDU-2852(集合第k大)

发表于 2018-03-26 | 分类于 ACM

题意

动态的集合求第k大

思路

添加一个数时就在树状数组里把那个数的位置+1,删除时就-1。这样sum[num]就表示集合中$\le num$的数有多少。
如果sum[num]<k表示第k大的数比num要大
反之sum[num]>=k 表示第k的数小于num,或等于num
二分时需要注意细节

哈尔滨理工大学第八届程序设计竞赛 D-小C的问题

发表于 2018-03-25 | 分类于 ACM

思路

依然是一个经典的思路——仔细观察问题,判断出真正的数据范围。遇到指数,阶乘等增加特别快的式子,可能当数据很小时就超出了另一个题目要求的范围,只需要处理较小的数据就可以了。
此题中如果长度为$len$的序列找不到三角形的话,增长最慢的情况是斐波那契数列,$len$为几十的时候就超出了$1e18$,只需要处理$len$小的情况,排序之后相邻三个暴力解决就可以了。

第13届广东工业大学ACM程序设计大赛 F-等式

发表于 2018-03-25

题意

给定$n$,求${1\over x}+{1\over y}={1\over n} (x\le y) $的解数(x、y、n均为正整数)

题解

化简方程
$$
nx+ny=xy
$$
代入
$$
\\
\begin{cases}
x=n+a\\
y=n+b
\end{cases}
\\
$$
得
$$
n^2=ab
$$
即为求$n^2$的约数个数,约数个数定理可解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <cstdio>
#include <cstring>
#include <algorithm>
#include "iostream"
#include <cmath>
#include <vector>
#include "set"
using namespace std;
#define FOP freopen("input.txt","r",stdin)
#define Met(x,y) memset(x,y,sizeof x)
typedef long long ll;
const ll INF = 1e18;
const ll MAXN =1e5+100;
const ll MOD = 1e9+7;
const ll LIM= 1e5;
int prime[LIM+100],cnt;
bool isprime[LIM+100];
void linear()
{
cnt=0;
memset(isprime,1,sizeof(isprime));
isprime[1]=false;
for(int i=2;i<=LIM;i++)
{
if(isprime[i])prime[cnt++]=i;
for(int j=0;j<cnt&&i*prime[j]<=LIM;j++)
{
isprime[i*prime[j]]=false;
if(i%prime[j]==0)break;
}
}
}

int main(int argc, char const *argv[]) {
// FOP;
linear();
ll T_T;
cin>>T_T;
while(T_T--)
{
ll n;
cin>>n;
ll po[100];
ll idx=0;
Met(po,0);
for(int i=0;i<cnt && prime[i]<=n;++i)
if(!(n%prime[i]))
{
while (n%prime[i]==0) {
n/=prime[i];
po[idx]++;
}
idx++;
}
if(n>1)
{
po[idx]=1;
idx++;
}
ll ans=1;
for(int i=0;i<idx;++i)
ans*=po[i]*2+1;
cout<<(ans/2)+1<<endl;
}
return 0;
}

12345

李聪

44 日志
2 分类
18 标签
© 2018 李聪
由 Hexo 强力驱动 }
|
主题 — NexT.Pisces v5.1.3
访问人数 总访问量 次