A - Sorted Arrays
贪心即可,从前往后看看能不能加入前一个的序列即可。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,a[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int las=3,ans=0;
for(int i=2;i<=n;i++){
if(a[i]==a[i-1]);
else if(a[i]>a[i-1]){
if(las==1) las=3,ans++;
else las=2;
}
else{
if(las==2) las=3,ans++;
else las=1;
}
}
ans++;
printf("%d\n",ans);
}
B - Hamiltonish Path
考虑 d f s dfs dfs树不存在横叉边,那么我们只需要找 d f s dfs dfs中一条极长的链即可,这样路径上只有树边和返祖边,满足条件。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
struct edge{
int y,nex;
}s[N<<1];
int first[N],len=0,n,m;
bool vis[N];
vector<int> V;
void ins(int x,int y){
s[++len]=(edge){y,first[x]};first[x]=len;
}
void dfs_1(int x){
vis[x]=true;
for(int i=first[x];i!=0;i=s[i].nex) if(!vis[s[i].y]){
dfs_1(s[i].y);break;
}
V.push_back(x);
}
void dfs_2(int x){
vis[x]=true;
V.push_back(x);
for(int i=first[x];i!=0;i=s[i].nex) if(!vis[s[i].y]){
dfs_2(s[i].y);break;
}
}
int main(){
scanf("%d %d",&n,&m);
int x,y;
for(int i=1;i<=m;i++){
scanf("%d %d",&x,&y);
ins(x,y),ins(y,x);
}
dfs_1(1);V.pop_back();dfs_2(1);
printf("%d\n",V.size());
for(int i=0;i<V.size();i++) printf("%d ",V[i]);
}
C - Ants on a Circle
好牛逼
首先序列上的问题就是无视每一次翻转方向,计算出每一只蚂蚁最后的位置之后,拍个序就知道每一只蚂蚁的位置。
环上的问题也是无视每一次的翻转方向,但是每一次有蚂蚁经过
0
0
0这条分界线的时候,编号为
1
1
1的位置就会偏移一位,计算偏移了几位,直接按顺序输出所有的解就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n,L,T;
int main(){
scanf("%d %d %d",&n,&L,&T);
int x,w,ans=1;
for(int i=1;i<=n;i++) {
scanf("%d %d",&x,&w);
if(w==2) w=-1;
a[i]=x+w*T;
if(a[i]<0) ans=(ans+n-(L-1-a[i])/L)%n;
else if(a[i]>=L) ans=(ans+a[i]/L)%n;
a[i]=(a[i]%L+L)%L;
}
if(ans==0) ans=n;
sort(a+1,a+1+n);
for(int i=ans;i<=n;i++) printf("%d\n",a[i]);
for(int i=1;i<ans;i++) printf("%d\n",a[i]);
}
D - Piling Up
好像和其他人的做法不太一样。
首先第一个拿出来的直接丢掉,
n
−
=
1
n-=1
n−=1即可,方案数最后乘上
2
2
2,最后一步肯定两种颜色都能取,所以也是给方案数最后乘上
2
2
2。先给
m
−
=
1
m-=1
m−=1。
那么现在就要考虑中间
m
m
m次的“放两个,取两个”的方案数。
相当于就有三种方案,放
B
B
B取
R
R
R,放
R
R
R取
B
B
B,不放不取。
考虑一条从
(
0
,
0
)
(0,0)
(0,0)开始的折线,当前点为
(
x
,
y
)
(x,y)
(x,y)
若放
R
R
R取
B
B
B,那么移动到
(
x
+
1
,
y
+
1
)
(x+1,y+1)
(x+1,y+1)
若放
B
B
B取
R
R
R,那么移动到
(
x
+
1
,
y
−
1
)
(x+1,y-1)
(x+1,y−1)
若不放不取,那么移动到
(
x
+
1
,
y
)
(x+1,y)
(x+1,y)
结论:
n
n
n的限制相当于使得
t
=
max
{
y
i
}
−
min
{
y
i
}
≤
n
t=\max\{y_i\}-\min\{y_i\}\leq n
t=max{yi}−min{yi}≤n
证明:考虑这样构造方案。
不放不取直接跳过
如果超过了最大值,那么从剩下的球中拿出来一个未染色的染成蓝色,最小值同理,而且容易证明没有其他方案。
如果没有超过,那么观察这条折线向左看去的第一条折线,这条折线一定与其是反向的。
这样,我们就可以拿出那时候放回去的球,再放回去一个相反颜色的球。
那么现在相当于要求一条长度为
m
m
m的折线,满足
t
≤
n
t\leq n
t≤n
稍微思考一下这就变成了一个简单的
D
p
Dp
Dp,我们只需要将最小值移回
0
0
0就可以了。
D
p
Dp
Dp时记录一下是否到过
0
0
0即可,注意不放不取的方案数为
2
2
2。
但博主考虑在第一次到达
0
0
0时计算答案,需要两个
D
p
Dp
Dp的答案拼起来。
#include<bits/stdc++.h>
using namespace std;
const int N=3010,mod=1000000007;
int f[N][N],g[N][N],n,m;
void ad(int&x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
int main(){
scanf("%d %d",&n,&m);n--;m--;
for(int i=0;i<=n;i++) f[0][i]=g[0][i]=1;
for(int i=0;i<m;i++){
for(int j=0;j<=n;j++) if(g[i][j]){
if(j!=0) ad(g[i+1][j-1],g[i][j]);
if(j!=n) ad(g[i+1][j+1],g[i][j]);
ad(g[i+1][j],g[i][j]);
ad(g[i+1][j],g[i][j]);
}
}
for(int i=0;i<m;i++){
for(int j=1;j<=n;j++) if(f[i][j]){
ad(f[i+1][j-1],f[i][j]);
if(j!=n) ad(f[i+1][j+1],f[i][j]);
ad(f[i+1][j],f[i][j]);
ad(f[i+1][j],f[i][j]);
}
}
int ans=0;
for(int i=0;i<=m;i++)
ad(ans,1ll*f[i][0]*g[m-i][0]%mod);
ad(ans,ans);ad(ans,ans);
printf("%d\n",ans);
}
E - Placing Squares
将一个长度为
l
l
l的区间的贡献
l
2
l^2
l2这样考虑:
在这个区间中有序的选出两个位置。
可以
O
(
n
)
D
p
O(n)Dp
O(n)Dp,使用矩阵快速幂优化。
有关键点的位置相当于不能新开一个区间。
时间复杂度就变成
O
(
3
3
log
n
+
3
2
m
log
n
)
O(3^3\log n+3^2m\log n)
O(33logn+32mlogn)
using namespace std;
const int mod=1000000007;
struct node{
int d[3][3];
}tot,X[30],Y;
int n,m;
node operator*(const node&a,const node&b){
node q;
for(int i=0;i<3;i++) for(int j=0;j<3;j++) q.d[i][j]=0;
for(int i=0;i<3;i++)
for(int k=0;k<3;k++)
for(int j=0;j<3;j++)
q.d[i][j]=(q.d[i][j]+1ll*a.d[i][k]*b.d[k][j])%mod;
return q;
}
void go(int x){
for(int i=0;i<30;i++) if(x&(1<<i)) tot=tot*X[i];
}
int main(){
scanf("%d %d",&n,&m);
X[0].d[0][0]=X[0].d[2][0]=X[0].d[0][1]=X[0].d[1][1]
=X[0].d[0][2]=X[0].d[2][1]=1;X[0].d[1][2]=X[0].d[2][2]=2;
Y=X[0];Y.d[2][0]=0;Y.d[2][2]=1;Y.d[2][1]=0;
tot.d[0][0]=tot.d[1][1]=tot.d[2][2]=1;
for(int i=1;i<30;i++) X[i]=X[i-1]*X[i-1];
int las=0,x=0;
for(int i=1;i<=m;i++){
scanf("%d",&x);
go(x-las);las=x+1;
tot=tot*Y;
}
go(n-las);
printf("%d\n",tot.d[0][2]);
}
F - Two Faced Cards
牛逼题
如何入手?
发现最后要让两两匹配。
若确定了方向,那么相当于
Z
i
≤
Y
i
Z_i\leq Y_i
Zi≤Yi,排个序就能解决。
但我们不排序,考虑换一种思路:
首先按
Y
i
Y_i
Yi将
Z
,
Y
Z,Y
Z,Y离散化,如何判定?
新建一个数组
T
T
T,给每一个
T
[
A
i
,
n
]
T_{[A_i,n]}
T[Ai,n]加
1
1
1,给
T
[
B
i
,
n
]
T_{[B_i,n]}
T[Bi,n]减
1
1
1。
若
T
i
T_i
Ti全非负即可。方案也很好构造,每次找到最大的
Z
i
,
Y
i
Z_i,Y_i
Zi,Yi取走即可。
那么这道题现在就变成了,先给所有的
T
[
A
i
,
n
]
+
1
T_{[A_i,n]}+1
T[Ai,n]+1,再给所有的
T
[
C
i
,
n
]
−
1
T_{[C_i,n]}-1
T[Ci,n]−1。
每次可以花费
1
1
1的代价给
[
B
i
,
A
i
)
+
1
[B_i,A_i)+1
[Bi,Ai)+1,定义
a
n
s
[
x
]
ans[x]
ans[x]为使得
T
i
≥
0
∣
i
∈
[
1
,
x
)
,
T
i
≥
−
1
∣
i
∈
[
x
,
n
]
T_i\geq 0|i\in[1,x),T_i\geq -1|i\in[x,n]
Ti≥0∣i∈[1,x),Ti≥−1∣i∈[x,n]的最小代价,第
i
i
i组答案即为
min
(
a
n
s
[
D
i
]
,
a
n
s
[
E
i
]
+
1
)
\min(ans[D_i],ans[E_i]+1)
min(ans[Di],ans[Ei]+1)
显然我们要将所有的
a
n
s
[
i
]
ans[i]
ans[i]处理出来。
考虑先求出
a
n
s
[
1
]
ans[1]
ans[1],可以采取正着或者倒着贪心,以倒着贪心为例。
每次遍历到一个点若发现
T
i
<
−
1
T_i<-1
Ti<−1,那么就找到左端点最小且包含该点的区间,使其
+
1
+1
+1,如果不存在,那么不存在方案,证明显然。
正着贪心其实是不行的,因为我们的这个
T
i
≥
0
∣
i
∈
[
1
,
x
)
,
T
i
≥
−
1
∣
i
∈
[
x
,
n
]
T_i\geq 0|i\in[1,x),T_i\geq -1|i\in[x,n]
Ti≥0∣i∈[1,x),Ti≥−1∣i∈[x,n]限制。
对于
a
n
s
[
x
]
ans[x]
ans[x]来说要使得前缀操作次数尽可能小,倒着贪心的时候可以做到这一点,正着贪心不行。
处理其他的
a
n
s
[
x
]
ans[x]
ans[x]正着贪心过去即可,每次找到一个未被使用包含该点右端点最大的区间,不存在则无解。
#include<bits/stdc++.h>
#define lowbit(x) (x&(-x))
using namespace std;
const int N=100010;
int a[N],b[N],c[N],d[N],n,q,ans[N];
vector<int> L[N],R[N];
int sum[N],m;
priority_queue<pair<int,int> > qs;
pair<int,int> X;
void add(int x,int t){
while(x<=m){
sum[x]+=t;
x+=lowbit(x);
}
}
int gs(int x){
int tot=0;
while(x){
tot+=sum[x];
x-=lowbit(x);
}
return tot;
}
int main(){
scanf("%d",&n);n++;
for(int i=1;i<n;i++) scanf("%d %d",&a[i],&b[i]);
for(int i=1;i<=n;i++) scanf("%d",&c[i]),d[i]=c[i];
sort(d+1,d+1+n);m=unique(d+1,d+1+n)-d-1;d[++m]=1e9+1;
for(int i=1;i<n;i++){
a[i]=lower_bound(d+1,d+1+m,a[i])-d;
b[i]=lower_bound(d+1,d+1+m,b[i])-d;
c[i]=lower_bound(d+1,d+1+m,c[i])-d;
add(a[i],1);add(c[i],-1);
if(b[i]<a[i]) R[a[i]-1].push_back(b[i]);
}
c[n]=lower_bound(d+1,d+1+m,c[n])-d;
add(c[n],-1);
for(int i=m;i>=1;i--) {
for(int j=0;j<R[i].size();j++)
qs.push(make_pair(-R[i][j],i));
int tmp=gs(i);
while(!qs.empty() && tmp<-1){
X=qs.top();qs.pop();
add(-X.first,1);add(X.second+1,-1);
tmp++;ans[0]++;
}
if(tmp<-1) {ans[0]=1e9;break;}
}
while(!qs.empty()) X=qs.top(),L[-X.first].push_back(X.second),qs.pop();
for(int i=1;i<=m;i++){
for(int j=0;j<L[i].size();j++)
qs.push(make_pair(L[i][j],i));
int tmp=gs(i);ans[i]=ans[i-1];
while(!qs.empty() && tmp<0){
X=qs.top();qs.pop();
add(X.second,1);add(X.first+1,-1);
tmp++;ans[i]++;
}
if(tmp<0) ans[i]=1e9;
}
scanf("%d",&q);
int x,y;
while(q--){
scanf("%d %d",&x,&y);
x=lower_bound(d+1,d+1+m,x)-d;
y=lower_bound(d+1,d+1+m,y)-d;
int mmin=min(ans[x-1],ans[y-1]+1);
printf("%d\n",mmin>=1e9?-1:n-mmin);
}
}