A.
题目大意:
设
f
(
x
)
f(x)
f(x)等于把
x
x
x颠倒后的数,给定
n
n
n,对于任意
0
<
x
<
=
n
0<x<=n
0<x<=n,求
x
f
(
f
(
x
)
)
\frac{x}{f(f(x))}
f(f(x))x有多少个不同的值。
n
<
=
1
0
100
,
T
<
=
100
n<=10^{100},T<=100
n<=10100,T<=100
分析:答案就是
n
n
n的位数,十分显然。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
const int N=107;
using namespace std;
int T;
char s[N];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%s",s+1);
int ans=strlen(s+1);
printf("%d\n",ans);
}
}
B.
题目大意:
开始时在
0
0
0点,第
i
i
i步可以选择跳到
(
x
+
i
)
(x+i)
(x+i)或者
(
x
−
1
)
(x-1)
(x−1),问跳到
n
n
n最小需要多少步。
n
<
=
1
0
6
,
T
<
=
1000
n<=10^6,T<=1000
n<=106,T<=1000
分析:
设
n
=
k
∗
(
k
+
1
)
/
2
+
d
n=k*(k+1)/2+d
n=k∗(k+1)/2+d,且
d
<
=
k
d<=k
d<=k。
如果
d
=
0
d=0
d=0,需要
k
k
k步;
如果我们在第
i
i
i步往后跳一步在往前跳一步,此时还是前进了距离
i
i
i,但是后续的每一步长度都能增加
1
1
1。如果我们刚开始就这么做,最多增加
k
−
1
k-1
k−1的距离。
所以当
0
<
d
<
k
0<d<k
0<d<k,需要
k
+
1
k+1
k+1步;
如果
d
=
k
d=k
d=k,需要跳
k
+
1
k+1
k+1步再往回跳一步,需要
k
+
2
k+2
k+2步。
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
const int maxn=1e6+7;
using namespace std;
int T,x;
int f[maxn];
int main()
{
scanf("%d",&T);
f[0]=0,f[1]=1;
for (int i=2;i<=2e3;i++)
{
int l=i*(i+1)/2-1;
int r=(i+1)*(i+2)/2-2;
if (r>1e6) r=1e6;
for (int j=l;j<=r;j++) f[j]=i+1;
f[l+1]=i;
if (r==1e6) break;
}
while (T--)
{
scanf("%d",&x);
printf("%d\n",f[x]);
}
}
C.
分析:题目意思比较复杂,结论就是输入
(
x
,
y
)
(x,y)
(x,y),输出
(
x
−
1
,
y
)
(x-1,y)
(x−1,y)。
x
,
y
<
=
1
0
6
,
T
<
=
1000
x,y<=10^6,T<=1000
x,y<=106,T<=1000
代码:
#include <iostream>
#include <cstdio>
const int maxn=2e5+7;
using namespace std;
int T,x,y;
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&x,&y);
printf("%d %d\n",x-1,y);
}
}
D.
初始有一个序列
a
a
a和一个数
x
x
x,每次可以选择一个
a
i
>
x
a_i>x
ai>x,交换他们,要求使得
a
a
a有序需要多少步。
n
<
=
500
,
T
<
=
1000
n<=500,T<=1000
n<=500,T<=1000
分析:
显然
x
x
x会越变越大,而且无法交换小于等于它的数。
那么小于等于
x
x
x的数一定从小到大排好而且在前面,如果大于
x
x
x的数未排好,那么就把
x
x
x放在小于等于它的数的后一位,并把那个位置的数换出来。此时就变成了一个新的原问题了,重复处理即可。
代码:
#include <iostream>
#include <cstdio>
const int maxn=507;
using namespace std;
int n,T,x,ans;
int a[maxn];
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&x);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
a[0]=-1;
ans=0;
int flag=1;
for (int i=1;i<=n;i++)
{
if (a[i]<=x)
{
if (a[i]<a[i-1])
{
flag=0;
break;
}
}
else
{
int flag1=1;
for (int j=i+1;j<=n;j++)
{
if (a[j]<a[j-1])
{
flag1=0;
break;
}
}
if (flag1) break;
swap(x,a[i]);
ans++;
}
}
if (flag) printf("%d\n",ans);
else printf("-1\n");
}
}
E.
题目大意:
给定平面上给定4个点,要求把它们移成一个平行与坐标轴的正方形,要求移动的距离(曼哈顿距离)和最小是多少。
x
,
y
<
=
1
0
9
,
T
<
=
1000
x,y<=10^9,T<=1000
x,y<=109,T<=1000
分析:
对正方形的边长进行三分,至于为什么是单峰我也不清楚。
对于半径为
d
d
d的正方形,显然可以横纵坐标分开算。
对于横坐标(纵坐标类似),假设四个点的横坐标排序后相邻两个的距离分别为
x
,
y
,
z
x,y,z
x,y,z,则
当
d
<
=
y
+
z
d<=y+z
d<=y+z,长方形左下角横坐标选择第二个点横坐标最优,否则长方形右下角横坐标选择第四个点横坐标最优。点之间的对应可以暴力全排列。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
int T,l,r,dx,dy,cnt;
LL ans;
LL a[5],b[5],c[5],d[5],e[5],f[5],v[5];
int t[30][5];
void dfs(int x)
{
if (x>4)
{
++cnt;
for (int i=1;i<=4;i++) t[cnt][i]=a[i];
return;
}
for (int i=1;i<=4;i++)
{
if (!v[i])
{
v[i]=1;
a[x]=i;
dfs(x+1);
v[i]=0;
}
}
}
LL dis(LL a,LL b,LL c,LL d)
{
return abs(a-c)+abs(b-d);
}
LL calc(LL k)
{
LL std=1e15,sum;
if (e[4]-e[2]>=k)
{
c[1]=e[2];
c[2]=e[2]+k;
c[3]=e[2];
c[4]=e[2]+k;
}
else
{
if (k-(e[4]-e[2])<e[2]-e[1])
{
c[1]=e[4]-k;
c[2]=e[4];
c[3]=e[4]-k;
c[4]=e[4];
}
else
{
c[1]=e[1];
c[2]=e[1]+k;
c[3]=e[1];
c[4]=e[1]+k;
}
}
if (f[4]-f[2]>=k)
{
d[1]=f[2];
d[2]=f[2];
d[3]=f[2]+k;
d[4]=f[2]+k;
}
else
{
if (k-(f[4]-f[2])<f[2]-f[1])
{
d[1]=f[4]-k;
d[2]=f[4]-k;
d[3]=f[4];
d[4]=f[4];
}
else
{
d[1]=f[1];
d[2]=f[1];
d[3]=f[1]+k;
d[4]=f[1]+k;
}
}
for (int i=1;i<=cnt;i++)
{
sum=0;
for (int j=1;j<=4;j++) sum+=dis(a[j],b[j],c[t[i][j]],d[t[i][j]]);
if (sum<std) std=sum;
}
return std;
}
void solve()
{
ans=1e15;
l=0,r=1e9;
while (r-l>=2)
{
int len=(r-l+1)/3;
int mid1=l+len-1,mid2=mid1+len;
LL p1=calc(mid1),p2=calc(mid2);
if (p1>p2)
{
ans=min(ans,p2);
l=mid1+1;
}
else
{
ans=min(ans,p1);
r=mid2-1;
}
}
for (int i=l;i<=r;i++) ans=min(ans,calc(i));
}
int main()
{
dfs(1);
scanf("%d",&T);
while (T--)
{
for (int i=1;i<=4;i++) scanf("%lld%lld",&a[i],&b[i]);
for (int i=1;i<=4;i++) e[i]=a[i];
sort(e+1,e+5);
for (int i=1;i<=4;i++) f[i]=b[i];
sort(f+1,f+5);
solve();
printf("%lld\n",ans);
}
}
F.
题目大意:看原题。
分析:显然前面的位字典序越小越好,设
b
[
i
]
b[i]
b[i]表示这一位有没有被操作过,考虑第
i
i
i位,有4种情况。
1.
b
[
i
]
=
0
b[i]=0
b[i]=0,则直接改变当前位,否则当前位不变。
2.如果
b
[
i
]
=
0
b[i]=0
b[i]=0,使自己字典序变小,否则不变;然后让后一位与当前位交换。显然这种情况,如果后一位是
a
a
a则比3优,否则比3劣。
3.与后一位交换;如果
b
[
i
+
1
]
=
0
b[i+1]=0
b[i+1]=0,使换过来的这一位字典序变小,否则换过来的位不变。
4.如果
b
[
i
]
=
0
b[i]=0
b[i]=0,使自己字典序变小,否则不变;然后使
i
+
1
i+1
i+1位与
i
+
2
i+2
i+2位交换,再使
i
+
2
i+2
i+2位与
i
i
i位交换。
在上述4中情况中选择字典序尽可能小的一种方案,否则尽可能选靠前的方案,因为同样情况省了操作数。注意因为一定要按原始的顺序一位一位处理,所以参与操作的所有位置
b
b
b值都要设为
1
1
1。代码中的
0
/
1
0/1
0/1与上述分析相反。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
const int maxn=507;
using namespace std;
int T;
int n,k;
int a[maxn],b[maxn];
char s[maxn];
int calc(int x,int p)
{
if (!p) return x;
if ((x==k) || (x==1)) return 1;
if (x==1) return 1;
return x-1;
}
int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&k);
scanf("%s",s+1);
for (int i=1;i<=n;i++)
{
a[i]=s[i]-'a'+1;
b[i]=1;
}
int p,minn;
for (int i=1;i<=n;i++)
{
minn=k+1;
if (calc(a[i],b[i])<minn)
{
minn=calc(a[i],b[i]);
p=1;
}
if ((i<=n-1) && (b[i+1]) && (a[i+1]<minn))
{
minn=a[i+1];
p=2;
}
if ((i<=n-1) && (b[i]) && (calc(a[i+1],b[i+1])<minn))
{
minn=calc(a[i+1],b[i+1]);
p=3;
}
if ((i<=n-2) && (b[i+1]) && (b[i+2]) && (a[i+2]<minn))
{
minn=a[i+2];
p=4;
}
printf("%c",'a'+minn-1);
if (p==1) b[i]=0;
if (p==2)
{
swap(a[i],a[i+1]);
swap(b[i],b[i+1]);
a[i+1]=calc(a[i+1],b[i+1]);
b[i]=b[i+1]=0;
}
if (p==3)
{
swap(a[i],a[i+1]);
b[i]=b[i+1]=0;
}
if (p==4)
{
swap(a[i+1],a[i+2]);
swap(b[i+1],b[i+2]);
swap(a[i],a[i+1]);
swap(b[i],b[i+1]);
a[i+1]=calc(a[i+1],b[i+1]);
b[i]=b[i+1]=b[i+2]=0;
}
}
printf("\n");
}
}
G.
题目:看原题。
分析:可以设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示执行到第
i
i
i行当前值为
j
j
j的答案。
如果第
i
i
i条语句是赋值为
x
x
x,那么
f
[
i
]
[
x
]
=
min
k
=
1
m
f
[
i
]
[
k
]
f[i][x]=\min_{k=1}^{m}f[i][k]
f[i][x]=mink=1mf[i][k]
f
[
i
]
[
j
]
=
f
[
i
−
1
]
[
j
]
+
V
,
j
≠
x
f[i][j]=f[i-1][j]+V,j≠x
f[i][j]=f[i−1][j]+V,j=x
对于一个一个
i
f
if
if语句,相当于一个新的类似于上面的dp,不过dp的初值不同。具体来说,程序开头的初值是
f
[
0
]
[
0
]
=
0
f[0][0]=0
f[0][0]=0,其余为
i
n
f
inf
inf。假设
i
f
if
if对应的数为
x
x
x,则初值为
f
[
i
]
[
x
]
=
f
[
i
−
1
]
[
x
]
f[i][x]=f[i-1][x]
f[i][x]=f[i−1][x],其余为
i
n
f
inf
inf。
考虑
i
f
if
if语句处理出来的结果与前面的结果合并,假设if语句开始的行为
p
p
p,结束的行为
q
q
q。
那么
f
[
q
]
[
j
]
=
m
i
n
(
f
[
p
−
1
]
[
j
]
,
f
[
q
−
1
]
[
j
]
)
,
j
≠
x
f[q][j]=min(f[p-1][j],f[q-1][j]),j≠x
f[q][j]=min(f[p−1][j],f[q−1][j]),j=x
就是可以选择跳过
i
f
if
if,或者不跳过
i
f
if
if。
f
[
q
]
[
x
]
=
f
[
q
−
1
]
[
x
]
f[q][x]=f[q-1][x]
f[q][x]=f[q−1][x]
以
x
x
x结束x不可能跳过
i
f
if
if。
我们注意到,对于每一行的语句而言,其拥有的状态并不多,而且 i f if if语句在执行后,这些状态就没有用了。所以我们考虑用set来维护状态。具体操作包括整体加 V V V,查找最小,单点修改,以及set合并,这里需要使用启发式合并进行处理。
代码:
#include <iostream>
#include <cstdio>
#include <set>
#include <map>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int n,m,k,cnt;
char str[10];
LL val[maxn];
struct node{
int op,x;
LL v;
}a[maxn];
struct rec{
int x;
LL w;
};
set <rec> s[maxn];
map <int,LL> h[maxn];
bool operator <(rec a,rec b)
{
if (a.w==b.w) return a.x<b.x;
return a.w<b.w;
}
bool operator ==(rec a,rec b)
{
return ((a.x==b.x) && (a.w==b.w));
}
int solve(int sta,LL d)
{
int id=++cnt;
set <rec> ::iterator it,it1;
s[id].insert((rec){sta,d});
h[id][sta]=d;
val[id]=0;
while (1)
{
k++;
if (a[k].op==1)
{
if (a[k].x!=m)
{
it=s[id].begin();
rec e=*it;
it=s[id].lower_bound((rec){a[k].x,h[id][a[k].x]});
rec f=*it;
if ((it!=s[id].end()) && (f==(rec){a[k].x,h[id][a[k].x]})) s[id].erase(it);
s[id].insert((rec){a[k].x,e.w-a[k].v});
h[id][a[k].x]=e.w-a[k].v;
}
val[id]+=a[k].v;
}
if (a[k].op==3) return id;
if (a[k].op==2)
{
int p=a[k].x;
it=s[id].lower_bound((rec){a[k].x,h[id][a[k].x]});
rec f=*it;
if ((it!=s[id].end()) && (f==(rec){a[k].x,h[id][a[k].x]}))
{
int q=solve(a[k].x,h[id][a[k].x]+val[id]);
if (s[id].size()>=s[q].size())
{
it1=s[q].begin();
while (it1!=s[q].end())
{
rec nw=*it1;
nw.w+=val[q]-val[id];
it=s[id].lower_bound((rec){nw.x,h[id][nw.x]});
rec e=*it;
if ((it!=s[id].end()) && (e==(rec){nw.x,h[id][nw.x]}))
{
if ((e.w>nw.w) || (nw.x==p))
{
s[id].erase(it);
s[id].insert((rec){nw.x,nw.w});
h[id][nw.x]=nw.w;
}
}
else
{
s[id].insert((rec){nw.x,nw.w});
h[id][nw.x]=nw.w;
}
it1++;
}
}
else
{
it1=s[id].begin();
while (it1!=s[id].end())
{
rec nw=*it1;
nw.w+=val[id]-val[q];
it=s[q].lower_bound((rec){nw.x,h[q][nw.x]});
rec e=*it;
if ((it!=s[q].end()) && (e==(rec){nw.x,h[q][nw.x]}))
{
if ((e.w>nw.w) && (nw.x!=p))
{
s[q].erase(it);
s[q].insert((rec){nw.x,nw.w});
h[q][nw.x]=nw.w;
}
}
else
{
s[q].insert((rec){nw.x,nw.w});
h[q][nw.x]=nw.w;
}
it1++;
}
id=q;
}
}
else
{
int dep=1;
while (dep)
{
k++;
if (a[k].op==2) dep++;
if (a[k].op==3) dep--;
}
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
scanf("%s",str+1);
if (str[1]=='s')
{
a[i].op=1;
scanf("%d%lld",&a[i].x,&a[i].v);
}
if (str[1]=='i')
{
a[i].op=2;
scanf("%d",&a[i].x);
}
if (str[1]=='e') a[i].op=3;
}
a[++n]=(node){3,0,0};
int q=solve(0,0);
set <rec> ::iterator it;
it=s[q].begin();
rec e=*it;
printf("%lld",e.w+val[q]);
}