C No More Inversions
首先,对于
1
,
2
,
.
.
.
,
k
−
1
,
k
,
k
−
1
,
.
.
.
,
2
,
1
1,2,...,k-1,k,k-1,...,2,1
1,2,...,k−1,k,k−1,...,2,1这样的排列,它只和
k
,
k
−
1
,
.
.
.
,
2
,
1
,
2
,
.
.
.
,
k
−
1
,
k
k,k-1,...,2,1,2,...,k-1,k
k,k−1,...,2,1,2,...,k−1,k的逆序数相同,其它情况逆序数都会更多。
其次,题目给的序列可以分成
1
,
2
,
.
.
.
,
t
1,2,...,t
1,2,...,t和
t
+
1
,
t
+
2
,
.
.
.
,
k
−
1
,
k
,
k
−
1
,
.
.
.
,
t
+
2
,
t
+
1
t+1,t+2,...,k-1,k,k-1,...,t+2,t+1
t+1,t+2,...,k−1,k,k−1,...,t+2,t+1两部分,第一部分放
t
t
t个数后,剩余的数在第二部分同样可以映射成一个排列,逆序数数量不变。因此第一部分放的数不能产生逆序数,故第一部分只能放
1
,
2
,
.
.
.
,
t
1,2,...,t
1,2,...,t,第二部分因为要字典序最大,把
t
+
1
,
.
.
.
,
k
t+1,...,k
t+1,...,k映射成
k
,
.
.
.
,
t
+
1
k,...,t+1
k,...,t+1即可。时间复杂度
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll f[N];
int n,k,a[N],p[N];
int main()
{
for(int i=1,j=1;i<N;i++,j+=2)
f[i]=f[i-1]+j;
int t;scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) p[i]=i;
for(int l=2*k-n,r=k;l<=r;l++,r--)
swap(p[l],p[r]);
for(int i=1;i<=k;i++)
printf(i==k?"%d\n":"%d ",p[i]);
}
}
D Program
注意进行
−
1
,
+
1
-1,+1
−1,+1这样的操作使得
x
x
x可取的值域总数连续的,操作序列看成一个只包含
−
1
,
+
1
-1,+1
−1,+1的序列
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1,a2,...,an,令
s
u
m
i
=
∑
j
=
1
i
a
i
sum_i=\sum\limits_{j=1}^ia_i
sumi=j=1∑iai。
则答案为
m
a
x
(
s
u
m
i
)
−
m
i
n
(
s
u
m
i
)
+
1
max(sum_i)-min(sum_i)+1
max(sumi)−min(sumi)+1。
当去掉了一个区间
L
,
R
L,R
L,R,
i
<
L
i<L
i<L的
s
u
m
i
sum_i
sumi不变,
i
>
R
i>R
i>R的
s
u
m
i
sum_i
sumi减去
s
u
m
R
−
s
u
m
L
−
1
sum_R-sum_{L-1}
sumR−sumL−1,只需要维护一个
s
u
m
i
sum_i
sumi的前缀后缀最大最小值即可。时间复杂度
O
(
n
)
O(n)
O(n)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,q,sum[N],mnl[N],mxl[N],mnr[N],mxr[N];
char s[N];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
if(s[i]=='+') sum[i]=1;
else sum[i]=-1;
for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
for(int i=0;i<=n+1;i++)
{
mnl[i]=mnr[i]=1e9;
mxl[i]=mxr[i]=-1e9;
}
for(int i=1;i<=n;i++)
{
mnl[i]=min(mnl[i-1],sum[i]);
mxl[i]=max(mxl[i-1],sum[i]);
}
for(int i=n;i>=1;i--)
{
mxr[i]=max(mxr[i+1],sum[i]);
mnr[i]=min(mnr[i+1],sum[i]);
}
while(q--)
{
int l,r;scanf("%d%d",&l,&r);
int x=0,y=0;
if(l>1)
{
x=min(x,mnl[l-1]);
y=max(y,mxl[l-1]);
}
int k=sum[r]-sum[l-1];
if(r<n)
{
x=min(x,mnr[r+1]-k);
y=max(y,mxr[r+1]-k);
}
printf("%d\n",y-x+1);
}
}
}
E Minimum Path
将原问题转述一下:一条路径中可以选择两条边,将一条边的权值变为
0
0
0,另一条边的权值
∗
2
*2
∗2,求对于点
i
=
{
2
,
3
,
.
.
.
,
n
}
i=\{2,3,...,n\}
i={2,3,...,n},
1
1
1和
i
i
i的最短路径。
这样求出的答案和原问题的答案等价。
用
d
i
s
i
,
0
/
1
,
0
/
1
dis_{i,0/1,0/1}
disi,0/1,0/1表示
1
1
1到
i
i
i,是否有一条边的权值变为
0
0
0,是否有一条边的权值被
∗
2
*2
∗2的最短距离。
点
i
i
i的答案为
m
i
n
(
d
i
s
i
,
0
,
0
,
d
i
s
i
,
1
,
1
)
min(dis_{i,0,0},dis_{i,1,1})
min(disi,0,0,disi,1,1)。
采用
d
i
j
k
s
t
r
a
dijkstra
dijkstra算法即可。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,m,tot,head[N],nex[N<<1],to[N<<1];
ll wi[N<<1];
void add(int u,int v,int w)
{
to[++tot]=v;nex[tot]=head[u];head[u]=tot;wi[tot]=w;
}
struct node
{
int u,x,y;
ll dis;
node(int u=0,int x=0,int y=0,ll dis=0):u(u),x(x),y(y),dis(dis){}
bool operator<(const node&o)const
{
return dis>o.dis;
}
};
priority_queue<node>q;
ll dis[N][2][2];
void dij()
{
memset(dis,inf,sizeof(dis));
dis[1][0][0]=0;
q.push(node(1,0,0,0));
while(!q.empty())
{
int u=q.top().u,x=q.top().x,y=q.top().y;
ll d=q.top().dis;
q.pop();
if(d>dis[u][x][y]) continue;
for(int i=head[u];i;i=nex[i])
{
int v=to[i];
if(dis[v][x][y]>dis[u][x][y]+wi[i])
{
dis[v][x][y]=dis[u][x][y]+wi[i];
q.push(node(v,x,y,dis[u][x][y]));
}
if(!x)
{
if(dis[v][1][y]>dis[u][x][y])
dis[v][1][y]=dis[u][x][y],q.push(node(v,1,y,dis[v][1][y]));
}
if(!y)
{
if(dis[v][x][1]>dis[u][x][y]+2*wi[i])
dis[v][x][1]=dis[u][x][y]+2*wi[i],q.push(node(v,x,1,dis[v][x][1]));
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v,w;scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dij();
for(int i=2;i<=n;i++)
printf(i==n?"%lld\n":"%lld ",min(dis[i][0][0],dis[i][1][1]));
}