A.Cancel the Trains
题意:
横竖都有101条轨道,告诉你两种轨道上都有速度为1的一些火车,问最少取消多少辆使得不出现相撞
题解:
显然判断一下初始会有多少相撞就取消多少辆火车即可。
int _,x,n,m;
int vis[105];
int main() {
for(scanf("%d",&_);_;_--){
n=rd();m=rd();
memset(vis,0,sizeof(vis));
int ans=0;
for (int i=1;i<=n;i++){
x=rd();vis[x]=1;
}
for (int i=1;i<=m;i++){
x=rd();ans+=vis[x];
}
cout<<ans<<endl;
}
return 0;
}
B.Suffix Operations
题意:
每次操作能对一个数列的后缀进行+1或-1,在最多修改一个数字的前提下使得最少的操作次数使得数列每个数全部相等
题解:
我们先考虑没有修改的条件下是多少个操作,显然就是两两数差的绝对值之和。
那单一的一个数对答案的贡献是多少呢?
若数列是这样的
3 2 1
那么显然两次+1操作就行
若数列是这样的
3 4 1
对于中间的4跟之前的2的区别在于,1变化到3经过了2,而不经过4,所以我们需要对于每一个数如果比相邻的数都大或都小,我们可以计算出他带来的答案贡献,判断出最大的之后,用初始答案减去即可。
int main() {
for(scanf("%lld",&_);_;_--){
n=rd();
for (int i=1;i<=n;i++)a[i]=rd();
ll ans=0,mx=0;
for (int i=n-1;i>=1;i--){
ans+=abs(a[i]-a[i+1]);
}
for (int i=1;i<=n;i++){
if (i==1)mx=max(mx,(ll)abs(a[1]-a[2]));
else if (i==n)mx=max(mx,(ll)abs(a[n]-a[n-1]));
else{
if (a[i]>a[i-1]&&a[i]>a[i+1])mx=max(mx,2*(a[i]-max(a[i-1],a[i+1])));
if (a[i]<a[i-1]&&a[i]<a[i+1])mx=max(mx,2*(min(a[i-1],a[i+1])-a[i]));
}
}
cout<<ans-mx<<endl;
}
return 0;
}
C.Triangles
题意:
给定一个由数字组成的矩阵,问每个数字组成的三角形的面积最大分别是多少。
每个数字的答案你可以最多修改一个元素来使得当前的三角形面积最大
三角形需满足,有一条边水平或竖直。
三角形面积*2处理
题解:
对于每个三角形来说只有两种情况。
在这里只讲边是水平的情况,竖直情况同理
情况一:
水平的边由原始点组成,那么第三个点可以随意取,为了最大化,我们都取在边界上
情况二:
水平的边由修改点来组成,那就对于每一行原始点肯定要么最左要么最右,然后我们让底最大,去找最大的高就行
代码写的复杂了点,其实可以不用vector存,直接记录每一行每一列的最前面和最后面的点就行
#define max(a,b) (((a)>(b))?(a):(b))
int _;
ll n;
char s[maxn][maxn];
vector<int>v1[10][maxn],v2[10][maxn];//v1 v2分别记录行列
ll ans[10];
ll mxl[10],mnl[10],mxc[10],mnc[10];
int main() {
for(scanf("%d",&_);_;_--){
scanf("%lld",&n);
for (int i=1;i<=n;i++)scanf("%s",s[i]+1);
for (int i=0;i<=9;i++)
for (int j=1;j<=n;j++)v1[i][j].clear(),v2[i][j].clear();
for (int i=0;i<=9;i++){
ans[i]=0;
mxl[i]=mxc[i]=0;
mnl[i]=mnc[i]=maxn;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++){
v1[s[i][j]-'0'][i].push_back(j);v2[s[i][j]-'0'][j].push_back(i);
mxl[s[i][j]-'0']=max(mxl[s[i][j]-'0'],1ll*i);//最大的行
mxc[s[i][j]-'0']=max(mxc[s[i][j]-'0'],1ll*j);//最大的列
mnl[s[i][j]-'0']=min(mnl[s[i][j]-'0'],1ll*i);//最小的行
mnc[s[i][j]-'0']=min(mnc[s[i][j]-'0'],1ll*j);//最小的列
}
for (int i=0;i<=9;i++){
for (int j=1;j<=n;j++){
if (v1[i][j].size()>=2)ans[i]=max(ans[i],1ll*(v1[i][j][v1[i][j].size()-1]-v1[i][j][0])*max(j-1,n-j));
if (v2[i][j].size()>=2)ans[i]=max(ans[i],1ll*(v2[i][j][v2[i][j].size()-1]-v2[i][j][0])*max(j-1,n-j));
if (v1[i][j].size()){
ll mxd=max(max(v1[i][j][0]-1,n-v1[i][j][0]),max(v1[i][j][v1[i][j].size()-1]-1,n-v1[i][j][v1[i][j].size()-1]));//最大的底
ans[i]=max(ans[i],mxd*max(j-mnl[i],mxl[i]-j));
}
if (v2[i][j].size()){
ll mxd=max(max(v2[i][j][0]-1,n-v2[i][j][0]),max(v2[i][j][v2[i][j].size()-1]-1,n-v2[i][j][v2[i][j].size()-1]));//最大的底
ans[i]=max(ans[i],mxd*max(j-mnc[i],mxc[i]-j));
}
}
}
for (int i=0;i<=9;i++)printf("%lld ",ans[i]);puts("");
}
return 0;
}
D.Checkpoints
题意:
给定一种游戏规则,若干个关卡,每次有1/2的概率通过,失败回到上个值为1的点。
给定n,让我们用最多2000个关,每次失败回到上一个值为1的关,构造出期望次数为n的情况。
题解:
先考虑单独n个关的期望次数是多少。
定义dp[n]为通过n关的期望次数
想要通过n关,首先肯定是通过了n-1关,然后有1/2的概率直接通过,1/2的概率失败回到原点。
如此反复
d
p
[
n
]
=
1
2
∗
(
d
p
[
n
−
1
]
+
1
)
+
1
4
∗
(
2
∗
d
p
[
n
−
1
]
+
2
)
.
.
.
.
.
dp[n]=\frac{1}{2}*(dp[n-1]+1)+\frac{1}{4}*(2*dp[n-1]+2).....
dp[n]=21∗(dp[n−1]+1)+41∗(2∗dp[n−1]+2).....
所以
d
p
[
n
]
=
∑
i
=
1
∞
i
2
i
∗
(
d
p
[
n
−
1
]
+
1
)
dp[n] = \sum_{i=1}^\infty\frac{i}{2^i}*(dp[n-1]+1)
dp[n]=i=1∑∞2ii∗(dp[n−1]+1)
我们对
i
2
i
\frac{i}{2^i}
2ii这个求数列收敛性,发现收敛于2,即极限为2
所以
d
p
[
n
]
=
2
∗
d
p
[
n
−
1
]
+
2
dp[n]=2*dp[n-1]+2
dp[n]=2∗dp[n−1]+2
运用一下数列的相关知识
而且易知
d
p
[
0
]
=
0
dp[0]=0
dp[0]=0
可得
d
p
[
n
]
=
2
n
+
1
−
2
dp[n]=2^{n+1}-2
dp[n]=2n+1−2
有了这个性质之后就随便做了
int _;
ll n;
int tot;
ll num[1000];
vector<int>v;
int main() {
ll t=4;
for (int i=1;1;i++){
num[i]=t-2;
if (num[i]>1e18){tot=i;break;}
t*=2;
}
for(scanf("%d",&_);_;_--){
n=rd();
if (n<=1){puts("-1");continue;}
v.clear();
n-=2;
v.push_back(1);
for (int i=tot;i>=1;i--){
while (n>=num[i]){
for (int j=1;j<i;j++)v.push_back(0);
v.push_back(1);
n-=num[i];
}
}
if (n)puts("-1");
else{
printf("%d\n",(int)v.size());
for (auto u:v)printf("%d ",u);
puts("");
}
}
return 0;
}
E.Dog Snacks
题意:给定一个树,初始在节点1。每次你都要选择去一个离当前节点最近的未访问过的节点。最后还需要回到原点。问每一步最大值的最小值为多少。
题解:
对于一个子树来说,肯定是遍历完了再出来。这个出来这一步,我们就要选择距离根节点最近的叶子节点来作为这个子树遍历的最后一个点。对于这个子树的子树们,包含最近叶子节点的肯定是最后遍历的,那么别的就是遍历完一个到另一个去,那么就要更新答案ans=max(ans,val[v]+2)
val数组代表,在当前子树里,从叶子到根的最小步。
但有一个例外:1节点为根时情况变得不同了。我们要选择最大的叶子节点最后遍历,为什么呢。因为如果不怎么做,我们更新答案是ans=max(ans,mx+2),如果选择他最后遍历就是ans=max(ans,mx+1).
ll _,cnt,ans;
ll n,val[maxn],d[maxn];
void dfs(int u,int fa){
for (int i=head[u];i;i=g[i].nxt){
int v=g[i].v;
if (v==fa)continue;
dfs(v,u);
}
if (d[u]==1&&u!=1){//叶子节点
val[u]=0;
}
else{
if (u!=1){//非根
ll mn=maxn;
for (int i=head[u];i;i=g[i].nxt){
int v=g[i].v;
if (v==fa)continue;
mn=min(mn,val[v]);
}
int flag=0;
for (int i=head[u];i;i=g[i].nxt){
int v=g[i].v;
if (v==fa)continue;
if (!flag&&val[v]==mn){
flag=1;
}
else{
ans=max(ans,val[v]+2);
}
}
val[u]=mn+1;
}
else{//根
ll mx=0;
for (int i=head[u];i;i=g[i].nxt){
int v=g[i].v;
if (v==fa)continue;
mx=max(mx,val[v]);
}
int flag=0;//最大值可能有好几个
for (int i=head[u];i;i=g[i].nxt){
int v=g[i].v;
if (v==fa)continue;
if (!flag&&val[v]==mx){
flag=1;
}
else{
ans=max(ans,val[v]+2);
}
}
ans=max(ans,mx+1);
//cout<<mx<<endl;
}
}
}
int main() {
for(_=rd();_;_--){
cnt=maxn;ans=1;
n=rd();
for (int i=1;i<=n;i++)head[i]=d[i]=0;tot=0;
for (int i=1,u,v;i<n;i++){
u=rd();v=rd();
add_edge(u,v);
add_edge(v,u);
d[u]++;d[v]++;
}
dfs(1,0);
//cout<<val[2]<<endl;
//ans=max(ans,val[1]);
cout<<ans<<endl;
}
return 0;
}