有
N
N
N 个点,
K
K
K种颜色。第
i
i
i 个点颜色为
b
i
b_i
bi 给定一个矩阵
S
S
S,其中
S
i
,
j
=
1
S_{i,j}=1
Si,j=1 表示
i
i
i 颜色能向
j
j
j 颜色连边。 其中
S
i
,
j
S_{i,j}
Si,j 不一定等于
S
j
,
i
S_{j,i}
Sj,i,
S
i
,
i
S_{i,i}
Si,i 也不一定等于
1
1
1 若第
i
i
i 个点向第
j
j
j 个点连边,则边权为
∣
i
−
j
∣
|i-j|
∣i−j∣
问你第一个点走到最后一个点的最短距离。
1
≤
N
≤
5
e
4
1\le N\le 5e4
1≤N≤5e4
1
≤
K
≤
50
1\le K\le 50
1≤K≤50
思路
首先,如果把所有的边都连上,时空复杂度都不够的。
我们(wo)一开始会想到:对于
i
i
i 这个点,对于所有的颜色
j
j
j,我们连边只连最近的那些颜色为
j
j
j 的点(左边一个右边一个,如果有的话)。 比如
b
[
]
=
{
2
,
3
,
3
,
3
}
b[]=\{2,3,3,3\}
b[]={2,3,3,3},且
S
2
,
3
=
1
S_{2,3}=1
S2,3=1,我们连边只连
<
1
→
2
>
<1\rightarrow 2>
<1→2>,省略了边
<
1
→
3
>
<1\rightarrow 3>
<1→3> 和
<
1
→
4
>
<1\rightarrow 4>
<1→4> 然后去跑一个最短路。 但是这样会
W
A
WA
WA
我们考虑为什么会
W
A
WA
WA,原因就是因为我们省略了一(hao)些(duo)边,会导致一些问题: 对于上面那个例子,
b
[
]
=
{
2
,
3
,
3
,
3
}
b[]=\{2,3,3,3\}
b[]={2,3,3,3},
S
2
,
3
=
1
S_{2,3}=1
S2,3=1 但是
S
3
,
3
=
0
S_{3,3}=0
S3,3=0 然后我们就无法从第一个点跑到第三个点和第四个点去了。
我们想到,如果 颜色
i
i
i 向颜色
j
j
j 连边了,且
p
o
s
i
<
p
o
s
j
pos_i<pos_j
posi<posj,那么对于所有的
p
≥
p
o
s
j
p\ge pos_j
p≥posj,且颜色仍为
j
j
j的点, 我们都能连边
<
p
o
s
i
→
p
>
<pos_i\rightarrow p>
<posi→p> 在上面的例子中:因为
<
1
→
2
>
<1\rightarrow 2>
<1→2>,且
b
2
=
b
3
=
b
4
b_2=b_3=b_4
b2=b3=b4,所以我们能连边
<
1
→
3
>
<1\rightarrow 3>
<1→3>,
<
1
→
4
>
<1\rightarrow 4>
<1→4>。
考虑到
p
a
<
p
b
<
p
c
p_a<p_b<p_c
pa<pb<pc,a连向b边权为
∣
p
b
−
p
a
∣
|p_b-p_a|
∣pb−pa∣,a连向c边权为
∣
p
c
−
p
a
∣
|p_c-p_a|
∣pc−pa∣ ,b连向c边权为
∣
p
c
−
p
b
∣
|p_c-p_b|
∣pc−pb∣。 所以这两种建图方式等价: (1)
<
a
→
b
>
<a\rightarrow b>
<a→b>,
<
a
→
c
>
<a\rightarrow c>
<a→c> (2)
<
a
→
b
>
<a\rightarrow b>
<a→b>,
<
b
→
c
>
<b\rightarrow c>
<b→c>
所以我们把
<
p
o
s
i
→
p
>
<pos_i\rightarrow p>
<posi→p> 从第一种建图方式变成第二种建图方式,看图吧:
然后从右往左连边的方式也差不多等价。 这样边数量级会变成
O
(
n
k
)
O(nk)
O(nk)
代码
时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =5e4+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;int n,k;int aa[MAX];int pre[55],suf[55];char can[55][55];
vector<pair<int,ll>>V[MAX];
ll dis[MAX];voiddijkstra(int s,int t){
dis[s]=0;
priority_queue<pair<ll,int>>Q;
Q.push(make_pair(0,s));while(!Q.empty()){int x = Q.top().second;
ll d =-Q.top().first;
Q.pop();if(d > dis[x])continue;for(auto it : V[x]){int y = it.first;
ll w = it.second;if(dis[y]> dis[x]+ w){
dis[y]= dis[x]+ w;
Q.push(make_pair(-dis[y],y));}}}}int qian[55],hou[55];intmain(){scanf("%d%d",&n,&k);for(int i =1;i <= n;++i){scanf("%d",&aa[i]);}for(int i =1;i <= k;++i){scanf("%s",can[i]+1);}// for(int i = 1;i <= k;++i){// for(int j = 1;j <= k;++j){// cout << can[i][j];// }// puts("");// }for(int i =1;i <= n;++i)dis[i]= LINF;for(int i =1;i <= k;++i){
pre[i]= suf[i]= INF;
hou[i]= n+1;
qian[i]=0;}
pre[aa[1]]=1;
suf[aa[n]]= n;for(int i =2;i <= n;++i){for(int j =1;j <= k;++j){if(pre[j]== INF)continue;if(can[aa[i]][j]=='1'){
V[i].push_back(make_pair(pre[j],i-pre[j]));
qian[j]=max(qian[j],pre[j]);}if(can[j][aa[i]]=='1'){
V[pre[j]].push_back(make_pair(i,i-pre[j]));
hou[aa[i]]=min(hou[aa[i]],i);}}
pre[aa[i]]= i;}for(int i = n-1;i >=1;--i){for(int j =1;j <= k;++j){if(suf[j]== INF)continue;if(can[aa[i]][j]=='1'){
V[i].push_back(make_pair(suf[j],suf[j]- i));
hou[j]=min(hou[j],suf[j]);}if(can[j][aa[i]]=='1'){
V[suf[j]].push_back(make_pair(i,suf[j]- i));
qian[aa[i]]=max(qian[aa[i]],i);}}
suf[aa[i]]= i;}// for(int i = 1;i <= n;++i){// for(auto it : V[i]){// show(i,it.first);// }// }// puts("---");for(int i =1;i <= k;++i){// show(qian[i],hou[i]);for(int j = hou[i]+1;j <= n;++j){if(aa[j]== i){
V[hou[i]].push_back(make_pair(j,j-hou[i]));}}for(int j = qian[i]-1;j >=1;--j){if(aa[j]== i){
V[qian[i]].push_back(make_pair(j,qian[i]-j));}}}// puts("---");// for(int i = 1;i <= n;++i){// for(auto it : V[i]){// show(i,it.first);// }// }dijkstra(1,n);// for(int i = 1;i <= n;++i){// show(dis[i]);// }if(dis[n]== LINF)puts("-1");elseprintf("%lld",dis[n]);return0;}/**
5 2
1 1 1 2 2
01
00
*/
D(新增)
题意
给定一个字符串
S
S
S。你需要构造一个(但不用给出) 字母表的一个排列
T
T
T 然后把
S
S
S 进行划分,使得每一个划分
S
i
S_i
Si 都是
T
T
T 的子序列。 求出最少的划分数。
是个状压dp。因为
20
20
20,最诱惑的当然是
20
×
2
20
20\times2^{20}
20×220了。 我们需要一种字母一种字母加进来。 为什么呢?因为假设原来加进来了集合为
U
U
U的字母,再加进去新字母
i
i
i 的花费和之前加进来集合
U
U
U 内字母的先后顺序无关。
设
f
[
U
]
f[U]
f[U] 表示考虑好字母的集合为
U
U
U 的最小花费 设
g
[
i
]
[
U
]
g[i][U]
g[i][U] 表示考虑好字母的集合为
U
U
U,新加进来字母
i
i
i 的额外花费 设
c
o
s
t
[
i
]
[
j
]
cost[i][j]
cost[i][j] 表示原来字符串
S
S
S 中,字母
i
i
i 和
j
j
j 的相邻个数。
我们最优划分是怎么样的呢?如果字母表排列为
a
b
ab
ab,字符串中子串为
b
a
ba
ba 的地方,这里必须划分开来,是必须的也是最优的。也就是说如果字母表就是
a
b
c
d
.
.
.
abcd...
abcd... 那么我们的花费为:
∑
i
=
1
26
∑
j
=
1
i
−
1
c
o
s
t
[
i
]
[
j
]
\sum_{i=1}^{26}\sum_{j=1}^{i-1}cost[i][j]
i=1∑26j=1∑i−1cost[i][j]
这里变成了数位
d
p
dp
dp,我们记录的是已经加进去的字母集合,每次新加进去一个字母,然后考虑
c
o
s
t
cost
cost 对答案的影响。
首先这个
c
o
s
t
[
i
]
[
j
]
cost[i][j]
cost[i][j] 很好获得,但是要注意字母是离散化之后的字母。
然后是这个
f
[
U
]
f[U]
f[U]。我们考虑新加进来字母
i
i
i 那么这个字母必须是新的,也就是
!
(
U
&
(
1
<
<
i
)
)
!(U\&(1<<i))
!(U&(1<<i)) 加进来之后,集合
U
U
U 变成了
U
∣
(
1
<
<
i
)
U|(1<<i)
U∣(1<<i) 花费呢?就是
g
[
i
]
[
U
∣
(
1
<
<
i
)
]
g[i][U|(1<<i)]
g[i][U∣(1<<i)] 也就是:
f
[
U
∣
(
1
<
<
i
)
]
=
min
{
f
[
U
]
+
g
[
i
]
[
U
∣
(
1
<
<
i
)
]
}
f[U|(1<<i)]=\min\{ f[U]+g[i][U|(1<<i)] \}
f[U∣(1<<i)]=min{f[U]+g[i][U∣(1<<i)]} 注意为什么不是加上
g
[
i
]
[
U
]
g[i][U]
g[i][U] 呢?因为相邻相同字母也是不合法的。
然后是这个
g
[
i
]
[
U
]
g[i][U]
g[i][U]。
g
[
i
]
[
U
]
=
g
[
i
]
[
U
−
(
1
<
<
k
)
]
+
c
o
s
t
[
i
]
[
j
]
其
中
∀
k
∈
U
g[i][U]=g[i][U-(1<<k)]+cost[i][j]\ \ \ \ 其中 \forall k\in U
g[i][U]=g[i][U−(1<<k)]+cost[i][j]其中∀k∈U 但是时间复杂度可能会变成
O
(
2
0
2
×
2
20
)
O(20^2\times2^{20})
O(202×220),因为我们是一个一个去找这个
k
k
k的。 我们把这个
k
k
k 给优化一下变成:
g
[
i
]
[
U
]
=
g
[
i
]
[
U
−
(
1
<
<
k
)
]
+
c
o
s
t
[
i
]
[
j
]
其
中
l
o
w
b
i
t
(
U
)
=
(
1
<
<
k
)
g[i][U]=g[i][U-(1<<k)]+cost[i][j]\ \ \ \ 其中lowbit(U)=(1<<k)
g[i][U]=g[i][U−(1<<k)]+cost[i][j]其中lowbit(U)=(1<<k) 时间复杂度变为
O
(
C
×
2
C
)
O(C\times2^{C})
O(C×2C)
代码
时间复杂度:
O
(
C
×
2
C
)
O(C\times2^{C})
O(C×2C)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;char ss[MAX];
set<char>S;
map<char,int>M;int aa[MAX];int cost[25][25];int f[(1<<21)];int g[25][(1<<21)];
unordered_map<int,int>GAO;intmain(){int n;scanf("%s",ss+1);
n =strlen(ss+1);for(int i =1;i <= n;++i){
S.insert(ss[i]);}int id =0;for(auto it : S){
M[it]= id++;}for(int i =1;i <= n;++i){
aa[i]= M[ss[i]];if(i >1){
cost[aa[i]][aa[i-1]]++;}}for(int i =1;i <(1<<id);++i){
f[i]= INF;}for(int i =0;i < id;++i){
GAO[(1<<i)]= i;}for(int i =1;i <(1<<id);++i){for(int j =0;j < id;++j){int k = GAO[(i&(-i))];
g[j][i]= g[j][i-(1<<k)]+ cost[j][k];}}for(int i =0;i <(1<<id);++i){for(int j =0;j < id;++j){if(i&(1<<j))continue;
f[i|(1<<j)]=min(f[i|(1<<j)],f[i]+g[j][i|(1<<j)]);}}printf("%d",f[(1<<id)-1]+1);// 可以想一下为什么要+1return0;}/**
*/
F
题意
给一个
N
×
N
N\times N
N×N 的
S
S
S 矩阵,其中
S
i
,
j
S_{i,j}
Si,j 为一个美丽值。 你需要选择一些地方,使得每一个
2
×
2
2\times2
2×2 的子矩阵中都有且仅有两个被选择的点。 你的收益为选择所有地方的美丽值。 求最大收益。
有
N
N
N 个点,每一次循环有
K
K
K 个操作。 第
i
i
i 个操作:交换位置为
a
i
a_i
ai 与
b
i
b_i
bi 的点。 问你无穷时间后, 每个点能到多少个不同的位置
2
≤
N
≤
1
0
5
2\le N\le 10^5
2≤N≤105
1
≤
K
≤
2
e
5
1\le K\le 2e5
1≤K≤2e5
思路
经过一次循环,就相当于获得了一个置换。 把置换连边,得到了一个置换群的图。去跑
d
f
s
dfs
dfs染色,每一个置换环的颜色相同。
然后从头操作,设
M
[
i
]
[
j
]
M[i][j]
M[i][j] 表示颜色为
i
i
i 的点能否跑到位置
j
j
j。 然后对于每一次操作,都去更新这个数组即可。
代码
时间复杂度:
O
(
N
+
K
)
O(N+K)
O(N+K)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =2e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;int per[MAX];int aa[MAX],bb[MAX];int nxt[MAX];
vector<int>V[MAX];int id;int col[MAX];voiddfs(int x){for(auto it : V[x]){if(!col[it]){
col[it]= id;dfs(it);}}}int shu[MAX];
unordered_map<int,unordered_map<int,bool>>M;intmain(){int n,m;scanf("%d%d",&n,&m);for(int i =1;i <= n;++i)per[i]= i;for(int i =1;i <= m;++i){scanf("%d%d",&aa[i],&bb[i]);swap(per[aa[i]],per[bb[i]]);}for(int i =1;i <= n;++i){
nxt[i]= per[i];//show(nxt[i]);
V[i].push_back(nxt[i]);}for(int i =1;i <= n;++i){if(!col[i]){
col[i]=++id;dfs(i);}}for(int i =1;i <= n;++i)per[i]= i,M[col[i]][i]= true,shu[col[i]]++;for(int i =1;i <= m;++i){if(!M[col[per[aa[i]]]][bb[i]]){
M[col[per[aa[i]]]][bb[i]]= true;
shu[col[per[aa[i]]]]++;}if(!M[col[per[bb[i]]]][aa[i]]){
M[col[per[bb[i]]]][aa[i]]= true;
shu[col[per[bb[i]]]]++;}swap(per[aa[i]],per[bb[i]]);}for(int i =1;i <= n;++i){printf("%d\n",shu[col[i]]);}return0;}/**
5 0
*/
H
题意
给一个长度为
N
N
N 的字符串
S
S
S 给定
Q
Q
Q 个询问,每次询问若
[
a
i
,
b
i
]
[a_i,b_i]
[ai,bi] 不染色,完成字符串的最小次数。 染色操作:字母越小,表示亮度越高。每一次染色可以选择一个区间,然后把区间的颜色都涂成
k
k
k。要求是区间内原本有的颜色必须
<
k
<k
<k 或者还没染色。
比如可以
[
.
.
.
]
→
[
A
A
.
]
→
[
A
B
B
]
[...]\rightarrow [AA.] \rightarrow [ABB]
[...]→[AA.]→[ABB]
1
≤
N
≤
1
0
5
1\le N\le 10^5
1≤N≤105
1
≤
Q
≤
1
0
5
1\le Q\le 10^5
1≤Q≤105
思路
中间一段不涂,等价于涂区间为
[
1
,
a
i
−
1
]
[1,a_i-1]
[1,ai−1] 的次数 + 涂区间尾
[
b
i
+
1
,
N
]
[b_i+1,N]
[bi+1,N] 的次数。
涂区间为
[
1
,
x
]
[1,x]
[1,x] 的答案怎么算呢?因为颜色重的可以覆盖颜色轻的,比如
[
.
.
.
.
]
→
[
A
A
A
A
]
→
[
A
B
B
A
]
[....]\rightarrow[AAAA]\rightarrow[ABBA]
[....]→[AAAA]→[ABBA] 我们可以想成一个这样的题目:
然后就变成了一个单调栈的问题了。 单调栈应该都会吧(
代码
时间复杂度:
O
(
N
+
Q
)
O(N+Q)
O(N+Q)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e5+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;char aa[MAX];int pp[MAX],ss[MAX];
stack<int>S;voidpre(int t){while(!S.empty())S.pop();
S.push(-INF);int res =0;for(int i =1;i <= t;++i){while(aa[i]< S.top()){
S.pop();}if(aa[i]> S.top()){
S.push(aa[i]);
res++;}
pp[i]= res;}}voidsuf(int s,int t){while(!S.empty())S.pop();
S.push(-INF);int res =0;for(int i = t;i >= s;--i){while(aa[i]< S.top()){
S.pop();}if(aa[i]> S.top()){
S.push(aa[i]);
res++;}
ss[i]= res;}}intmain(){int n,q;scanf("%d%d%*c",&n,&q);scanf("%s%*c",aa+1);pre(n);suf(1,n);while(q--){int ta,tb;scanf("%d%d",&ta,&tb);printf("%d\n",pp[ta-1]+ ss[tb+1]);}return0;}/**
8 3
ABBAABCB
3 6
1 4
1 8
*/
I
题意
有
N
N
N 头牛,高度为
a
i
a_i
ai 有
N
N
N 个房子,高度为
b
i
b_i
bi 牛必须住到高度不低于它的房子。 每个房子只能住一头牛。 问你所有牛都住进去的方案数。
1
≤
N
≤
20
1\le N\le 20
1≤N≤20
思路
一开始状压
d
p
dp
dp 要么
T
L
E
TLE
TLE 要么
M
L
E
MLE
MLE 只要 两个数组降序排序。 考虑
d
p
[
i
]
dp[i]
dp[i] 表示前
i
i
i 头牛都住进去的合法方案数。
y
a
n
[
i
]
=
j
yan[i]=j
yan[i]=j 表示第
i
i
i 头牛最矮住的进去第
j
j
j 个房子。 那么第
i
i
i 头牛住进去的方案数多少呢?
(
y
a
n
[
i
]
−
(
i
−
1
)
)
(yan[i]-(i-1))
(yan[i]−(i−1)) 那么就得到了石子:
d
p
[
i
]
=
d
p
[
i
−
1
]
×
(
y
a
n
[
i
]
−
(
i
−
1
)
)
dp[i]=dp[i-1]\times(yan[i]-(i-1))
dp[i]=dp[i−1]×(yan[i]−(i−1))
代码
时间复杂度:可以到
O
(
N
log
N
)
O(N\log N)
O(NlogN)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e7+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;int aa[25],bb[25];
ll dp[25];
ll yan[25];intmain(){int n;scanf("%d",&n);for(int i =1;i <= n;++i)scanf("%d",&aa[i]);for(int i =1;i <= n;++i)scanf("%d",&bb[i]);sort(aa+1,aa+1+n,greater<int>());sort(bb+1,bb+1+n,greater<int>());for(int i =1;i <= n;++i){for(int j =1;j <= n;++j){if(aa[i]<= bb[j])yan[i]= j;elsebreak;}}
dp[0]=1;for(int i =1;i <= n;++i){
dp[i]= dp[i-1]*(yan[i]-(i-1));}printf("%lld",dp[n]);return0;}/**
4
1 1 1 1
1 1 1 1
*/
J
题意
求
⊕
L
M
F
(
i
)
\oplus LMF(i)
⊕LMF(i) 其中
L
M
F
(
i
)
LMF(i)
LMF(i) 表示
i
i
i 的最小质因子。
2
≤
N
≤
1
0
7
2\le N\le 10^7
2≤N≤107
思路
欧拉筛就是通过每个数的最小质因子来实现线性筛的。
代码
时间复杂度:
O
(
N
)
O(N)
O(N)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e7+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;
bool vis[MAX];int prime[MAX];int cnt;int ans =0;voidshai(int n){for(int i =2;i <= n;++i){if(!vis[i])prime[++cnt]= i,ans^=i;for(int j =1;j <= cnt && i * prime[j]<= n;++j){
vis[i*prime[j]]=1;//show(i*prime[j],prime[j]);
ans^=prime[j];if(i%prime[j]==0)break;}}}intmain(){int n;scanf("%d",&n);shai(n);printf("%d",ans);return0;}/**
2 2
1 2
00
00
2 1
1 1
1
*/
K
题意
N
N
N 个数,每个数有编号
B
i
B_i
Bi 需要划分成最多数量的区间,使得奇数编号的区间,区间内的编号和为偶数;偶数编号的区间,区间内的编号和为奇数。
2
≤
N
≤
1
0
3
2\le N\le 10^3
2≤N≤103
思路
只要考虑你现在有多少个奇数和多少个偶数编号即可。 每次可以取两个点合并,就三种情况。 记忆化,能否获得 奇数点有
x
x
x 个,偶数点有
y
y
y 个的情况。 简单的
b
f
s
bfs
bfs 即可。 然后合法的答案:
x
=
y
x=y
x=y 或者
x
+
1
=
y
x+1=y
x+1=y
然后就可以不用分类讨论了
代码
时间复杂度:
O
(
N
2
)
O(N^2)
O(N2)
#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;typedeflonglong ll;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}constint MAX =1e7+50;constint MOD =1e9+7;constint INF =0x3f3f3f3f;const ll LINF =0x3f3f3f3f3f3f3f3f;constdouble EPS =1e-5;int shu[2];
bool can[1050][1050];int ans =1;voidbfs(int s,int t){
queue<pair<int,int>>Q;
Q.push(make_pair(s,t));while(!Q.empty()){int x = Q.front().first;int y = Q.front().second;//show(x,y);
Q.pop();if(x>=2&&!can[x-2][y+1]){
can[x-2][y+1]= true;
Q.push(make_pair(x-2,y+1));}if(x && y &&!can[x][y-1]){
can[x][y-1]= true;
Q.push(make_pair(x,y-1));}if(y>=2&&!can[x][y-1]){
can[x][y-1]= true;
Q.push(make_pair(x,y-1));}if(x == y){
ans =max(ans,x+y);}elseif(x +1== y){
ans =max(ans,x+y);}}}intmain(){int n;scanf("%d",&n);for(int i =1;i <= n;++i){int t;scanf("%d",&t);
shu[(t&1)]++;}
can[shu[1]][shu[0]]= true;bfs(shu[1],shu[0]);printf("%d",ans);return0;}/**
4
1 1 1 1
1 1 1 1
*/