A. Got Any Grapes?
煎蛋的谈心…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
int x,y,z,a,b,c;
bool jude(){
if(a<x)return false;
a-=x;b+=a;
if(b<y)return false;
b-=y;c+=b;
if(c<z)return false;
return true;
}
int main()
{
cin>>x>>y>>z>>a>>b>>c;
if(jude())printf("YES\n");
else printf("NO\n");
return 0;
}
B. Yet Another Array Partitioning Task
一开始YY思路,发现max值一定是数列中前m*k个数的和(没有证明出来,发现自己想不出任何反例,那么就立马敲之)。
思路:先求出数列中最大的前m*k个数,然后按这些数划分为k个区间,如果一个区间内含有m个数列中最大的前m*k个数,那么继续划分下一个区间。注意!!!假设最大的前m*k个数中的最小值为2,但是只需要3个,而数列中一共有5个,这时候要统计需要的最小值的个数。
注释很详细…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const int Max_n=2e5+10;
int n,m,k;
int a[Max_n],b[Max_n];
int main()
{
cin>>n>>m>>k;
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(a,a+n);
int f=a[n-m*k]; // 找到最小的数f
int c=0;
for(int i=n-m*k;i<n;i++){ // 求最小的数f的个数c
if(a[i]==f)c++;
else break;
}
ll ans=0;
for(int i=n-m*k;i<n;i++){
ans+=a[i];
}
cout<<ans<<endl;
// t记录当前区间已经划分了t个>=f的数,如果t=m的话,当前区间划分好了,开始继续划分下一个
int t=0,kk=0;
for(int i=0;i<n;i++){ // 然后一路贪心
if(b[i]>f)t++;
if(b[i]==f&&c>0){t++;c--;}
if(t==m){printf("%d ",i+1);t=0;kk++;}
if(kk==k-1)break;
}
putchar('\n');
return 0;
}
C. Trailing Loves (or L’oeufs?)
[官方题解]
根据唯一分解定理,假设b可以分解为
p
1
q
1
∗
p
2
q
2
∗
.
.
.
∗
p
m
q
m
p1^{q_1} * p_2^{q_2 }*...* p_m^{q_m}
p1q1∗p2q2∗...∗pmqm,其中
p
1
,
p
2
.
.
.
p
m
p_1,p_2...p_m
p1,p2...pm为质数。
同时,
n
!
n!
n!也可以分解成相应的形式:
p
1
r
1
∗
p
2
r
2
∗
.
.
.
∗
p
m
r
m
∗
k
p_1^{r_1} * p_2^{r_2} *...* p_m^{r_m}*k
p1r1∗p2r2∗...∗pmrm∗k,
k
k
k和
p
i
p_i
pi都互质,不管它。
那么,最终的答案即为
m
i
n
{
r
i
p
i
}
min\{\frac{ri}{pi}\}
min{piri}。类比十进制就更加清晰明了啦…
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f3f;
ll n,b;
int main()
{
cin>>n>>b;
int f=sqrt(b+0.5);
ll ans=inf;
for(int i=2;i<=f;i++){
if(b%i==0){
ll a=0,c=0;
while(b%i==0)b/=i,c++;
ll m=n;
while(m)a+=m/i,m/=i;
ans=min(ans,a/c);
}
}
if(b>1){
ll a=0;
while(n)a+=n/b,n/=b;
ans=min(ans,a);
}
cout<<ans<<endl;
return 0;
}
D. Flood Fill
思路1:
区间dp(想出来了,可是做的时候没有想出来下面这句话)
对于区间[l,r],最优的方案要么全变成l处的颜色,要么全变为r处的颜色。
- 设状态:f(i,j,0/1)表示区间[i,j]变为i/j处的颜色的最少操作次数。
- 状态转移:显然得f(i,j,0)由f(i+1,j,0/1)转移来,f(i,j,1)由f(i,j-1,0/1)转移来。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int Max_n=5500;
int n;
int a[Max_n];
int dp[Max_n][Max_n][2];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int d=1;d<n;d++){
for(int i=1,j;(j=i+d)<=n;i++){
dp[i][j][0]=min(dp[i+1][j][0]+(a[i]!=a[i+1]),dp[i+1][j][1]+(a[i]!=a[j]));
dp[i][j][1]=min(dp[i][j-1][0]+(a[j]!=a[i]),dp[i][j-1][1]+(a[j]!=a[j-1]));
}
}
printf("%d\n",min(dp[1][n][0],dp[1][n][1]));
return 0;
}
思路2:
区间dp(没想出来…)
- 先把颜色块压缩,即相同颜色块合并。[5,2,2,1]->[5,2,1]
- 设状态:dp[l][r]表示合并[l, r]之间的颜色块的最小花费。
- 状态转移:
dp[i][j]=dp[i+1][j-1]+1 (a[i]==a[j])
dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1) (a[i]!=a[j])
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int Max_n=5500;
int n;
int a[Max_n];
int dp[Max_n][Max_n];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int k=unique(a+1,a+n+1)-a;
for(int i=1;i<k;i++)dp[i][i]=0;
for(int d=1;d<k-1;d++){
for(int i=1,j;(j=i+d)<k;i++){
if(a[i]==a[j])dp[i][j]=dp[i+1][j-1]+1;
else dp[i][j]=min(dp[i+1][j]+1,dp[i][j-1]+1);
}
}
printf("%d\n",dp[1][k-1]);
return 0;
}
思路3:
最长回文子序列求解
方法:先把初始颜色序列去重,设去重后长度为n,然后找最长回文子序列len,答案就是n-ceil(len/2)。
找最长回文子序列的方法是原序列和原序列的反转求LCS的长度。
以下摘自博主维和战艇机:
假设n个颜色块的颜色都各不相同,那么合并需要n - 1次。如果出现了[1, 2, 3, 1]这种情况,那么合并次数可以少一次。如果是[4, 1, 2, 3, 4, 3, 2, 1]这种情况,那么先合并3到3之间,再合并2到2之间,再合并1到1之间比先合并4到4之间更优。如果我们把压缩序列上相同并且相距最近的两个数看成区间,每出现一个区间答案会减一,所以答案是n - 1 - 不相交的最多的区间的数目(区间在端点相交不算相交,包含关系不算相交)。比如[4, 1, 2, 3, 4, 3, 2, 1]这种情况,有4个区间。4:[1, 5] , 1: [2, 8], 2: [3, 7], 3: [4, 6]。显然,保留 [2, 8], [3, 7], [4, 6]三个区间最优,答案是8 - 1 - 3 = 4。那么怎么求不相交的最多的区间的数目呢?这个区间数目是压缩序列和反向压缩序列的的LCS / 2。例如[1, 2, 3, 1]和[1, 3, 2, 1]的LCS是3,区间个数是1。为什么呢?求lCS的过程中,我们每匹配成功一次,相当于区间的左端点找到的一个可以匹配的区间的右端点,或者是一个右端点找到一个对应的左端点,而匹配过程中是一个从前往后, 一个从后往前,所以匹配成功的区间肯定不相交。这个过程之后,每个区间被算了2次,并且会匹配出一个左端点等于右端点的区间,所以要除以2。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int Max_n=5500;
int n;
int a[Max_n],b[Max_n];
int dp[Max_n][Max_n];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int k=unique(a+1,a+n+1)-a;
for(int i=1;i<k;i++)b[i]=a[i];
reverse(b+1,b+k);
for(int i=1;i<k;i++){
for(int j=1;j<k;j++){
if(a[i]==b[j])dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
printf("%d\n",k-2-dp[k-1][k-1]/2);
return 0;
}
DEF…(纳尔渣渣…)
做饭饭去喽…