A. Little C Loves 3 II
题意:
给n * m的网格,每次可以选择距离为
∣
x
i
−
x
j
∣
+
∣
y
i
−
y
j
∣
|x_i−x_j|+|y_i−y_j|
∣xi−xj∣+∣yi−yj∣ 的两点标记,问最多可以标记多少个点。
解:
找规律题
关键点:n,m取(1,6)(2,4)(3,4)(2,5)时都能把整个网格全部标记完。
那么可以推得,当n,m为(奇,偶)(偶,偶) 时答案为n*m,即全部标记,而为(奇,奇)时,答案为 n * m - 1。[奇代表大于3的奇数,偶代表大于2的偶数]
证:
因为(1,6)可以完全标记,所以只要一边是6的倍数,另一边无论取几都能完全标记。
因为(3,3)(3,5)只有一个没标记,(3,4)(3,6)全标记,所以只要(3,偶>=4)一定可以由(3,4)(3,6)构成,一定全标记,而(3,奇>=5)一定可以由(3,3)和一个(3,偶>=4)构成,一定只有一个没标记。
当n为大于3的奇数,m为大于2的偶数。
m一定可以由若干个4,6构成,而n一定可以由若干个2,3构成。那么把方阵拆分成由4或其倍数和由6或其倍数构成的两部分,6的部分前面已证肯定能全部标记,而4的部分可以全都拆分为(2,4)(3,4)也可以全部标记,所以可以全部标记。
当n,m都为大于2的偶数。
n,m都一定可以由若干个4,6构成,与n奇m偶同理。
当n,m都为大于3的奇数。
而当n,m大于3,较大的奇数n一定可以拆分为一个3和一个偶数,(奇,偶)前面已证全标记,(3,奇)也已知只有一个没标记。
所以当n,m都大于等于3的时候直接根据奇偶输出n * m或者n * m-1,否则直接根据n=1,2,3分别计算。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll f[10]= {0,0,0,0,2,4};
ll ans[4][4]={0,0,0,0,0,0,0,0,0,0,0,4,0,0,4,8};
int main()
{
ll n,m;
while(cin>>n>>m)
{
ll mx=max(n,m);
ll mi=min(n,m);
if(n<=3&&m<=3)
cout<<ans[n][m]<<endl;
else if(n>=3&&m>=3)
{
if(n%2==0||m%2==0)cout<<n*m<<endl;
else cout<<n*m-1<<endl;
}
else if(mi==1)
{
cout<<mx/6*6+f[mx%6];
}
else if(mi==2)
{
if(mx==4)cout<<n*m<<endl;
else if(mx==5)cout<<n*m<<endl;
else if(mx==6)cout<<n*m<<endl;
else if(mx==7)cout<<12<<endl;
else cout<<n*m<<endl;
}
}
}
B. Anton and Making Potions
题意:
求制作n瓶药的最小时间,已知每瓶药需要花费x时间。现在有两种魔法可以加快制药,最开始总共有魔力s,第一种魔法有m个,是把每瓶药花费时间变成
a
i
a_i
ai,花费
b
i
b_i
bi魔力,第二种魔法有k个,是直接生成
c
i
c_i
ci个药,花费
d
i
d_i
di魔力。每种魔法最多用一个。
解:
字多水题
直接暴力遍历第一种魔法的每一个,然后二分第二种魔法找到能用剩余的魔力制药最多的。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[200005],b[200005],c[200005],d[200005];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n,m,k,x,s;
cin>>n>>m>>k>>x>>s;
for(int i=1;i<=m;++i)
cin>>a[i];
for(int i=1;i<=m;++i)
cin>>b[i];
for(int i=1;i<=k;++i)
cin>>c[i];
for(int i=1;i<=k;++i)
cin>>d[i];
ll j=upper_bound(d+1,d+k+1,s)-d;
ll ans=j!=1&&j<=k?(n-c[j-1])*x:j==1?n*x:(n-c[k])*x;
for(int i=1;i<=m;i++)
{
if(b[i]<=s){
j=upper_bound(d+1,d+k+1,s-b[i])-d;
j=j>1&&j<=k?j-1:j<=1?0:k;
ans=min(ans,(n-c[j])*a[i]);
}
}
cout<<ans<<endl;
}
F. Knight Tournament
题意:n个人排成一列,每次给一个区间
【
L
,
R
】
【L,R】
【L,R】和一个
i
(
L
<
=
i
<
=
R
)
i(L<=i<=R)
i(L<=i<=R),把这个区间内除了位置i和之前已经操作过的位置以外全都变成i,要求输出操作全部结束每个数是什么。
解:
我看到有用并查集做的,还有直接变换下标然后遍历,其实主题思路都差不多。
#include<bits/stdc++.h>
using namespace std;
int f[300005],a[300005];
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n,m,l,r,x,tmp;
cin>>n>>m;
for(int i=1; i<=n; i++)
f[i]=i+1;
for(int i=1; i<=m; i++)
{
cin>>l>>r>>x;
for(int j=l; j<x; j=tmp)
{
tmp=f[j];
if(!a[j])a[j]=x;
f[j]=x;
}
for(int j=x+1; j<=r; j=tmp)
{
tmp=f[j];
if(!a[j])
a[j]=x;
f[j]=f[r];
}
}
for(int i=1; i<=n; i++)if(i!=n)cout<<a[i]<<" ";
else cout<<a[i]<<'\n';
}