【分析】
这一题只要仔细看题目的话应该是简单的,也相信大家不是来看我怎么花式讲水题的,就直接贴代码了。
【代码】
#include
using namespace std;
#define M 40
char a[M],b[M];
int n,m,f=1;
int pos1,pos2;
char mp[M][M];
int main(){
scanf("%s",a+1);
scanf("%s",b+1);
n=strlen(a+1);
m=strlen(b+1);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)mp[i][j]='.';
}
for(int i=1;f&&i<=n;i++){
for(int j=1;f&&j<=m;j++){
if(a[i]==b[j]){
f=0;
pos1=j;
pos2=i;
}
}
}
for(int i=1;i<=n;i++)mp[pos1][i]=a[i];
for(int i=1;i<=m;i++)mp[i][pos2]=b[i];
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++)printf("%c",mp[i][j]);
puts("");
}
return 0;
}
【分析】
考虑到答案存在单调性,所以就直接二分咯。
想要1A的话还是要注意算md的时候有可能爆int。
【代码】
#include
using namespace std;
#define ll long long
#define M 1000005
int a[M];
int n,m;
int l,r;
int res;
bool chk(int h){
ll hav=0;
for(int i=1;i<=n;i++){
if(a[i]<=h)continue;
hav+=a[i]-h;
}
return hav>=m;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
r=max(r,a[i]);
}
while(!(l>r)){
int md=1ll*(l+r)/2;
if(chk(md)){
res=md;
l=md+1;
}else r=md-1;
}
printf("%d\n",res);
return 0;
}
【分析】
知道题可以考虑贪心和dp,但是,我并没有找出容易的贪心做法,那就换一个路子,dp。
我们定义dp[i][j]表示将1~i全部变成j需要的最小个数。
那么转移方程也就比较容易推出了。
但是比赛的时候没有将这题1A,原因是RE了…
【代码】
#include
using namespace std;
#define M 1000005
int dp[2][M];//B 0;A 1
bool mk[M];
char a[M];
int n;
int main(){
scanf("%d",&n);
scanf("%s",a+1);
for(int i=1;i<=n;i++){
bool f=(a[i]=='A');
dp[f][i]=min(dp[f][i-1],dp[!f][i-1]+1);
dp[!f][i]=min(dp[f][i]+1,dp[!f][i-1]+1);
}
printf("%d",dp[1][n]);
return 0;
}
【分析】
花了半天时间看英文题似懂非懂。感谢小C的中文题意。
我们发现,想要组成这样一个首尾相连的矩形,唯一有用的只是一个单词的开头和结尾的字母。
既然有四个起点和终点,那么我们就枚举终态。
不过还要考虑到一些重复算的相同的部分。如何将他们去掉,就看大家各显神通了。
【代码】
#include
using namespace std;
#define M 100005
#define ll long long
char a[M][15];
int n,m;
ll ans;
int cnt[28][28];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
m=strlen(a[1]+1);
for(int i=1;i<=n;i++){
int x=a[i][1]-'A';
int y=a[i][m]-'A';
cnt[x][y]++;
}
for(int i=0;i<26;i++){
for(int j=0;j<26;j++){
for(int k=0;k<26;k++){
for(int p=0;p<26;p++){
int you[28][28];
memset(you,0,sizeof(you));
ll now=cnt[i][j];
you[i][j]++;
now*=1ll*(cnt[j][k]-you[j][k]);
you[j][k]++;
now*=1ll*(cnt[p][k]-you[p][k]);
you[p][k]++;
now*=1ll*(cnt[i][p]-you[i][p]);
if(now<=0)continue;
ans+=now;
}
}
}
}
cout<return 0;
}
【分析】
比赛的时候完全没什么想法。直接敲了30分。
比赛结束后听jiedai大神的思路,感觉智商又下线了。
啦啦啦,我又回来了!
以下是sonytoy大神的证明
**我们假设已知当前积木 i 质量 mass ,之前所有积木质量M,
以积木 i 的左端点为原点,设 i−1 块积木构成的整体的重心在X轴的坐标为 x , x∈[0,2]
新的重心横坐标
NewCentre=M∗x+1∗massM+mass=M∗x+massM+mass
新的最右端
Right=Max(2,x+dp[i−1])
所以
dp[i]=Max(2,x+dp[i−1])−M∗x+massM+mass
=Max(2∗M+2∗mass,x∗(M+mass)+dp[i−1]∗(M+mass))−M∗x−massM+mass
=Max((2−x)∗M+mass,(x−1)∗mass+dp[i−1]∗(M+mass))M+mass
可以发现, Max() 中的两个式子,一个是单调递增,一个是单调递减的,均满足单调性,因此直接考虑 x=0,2 完全是可以的,换句话说,即一个是重心在左端点,一个是重心在右端点。所以答案是在这两个地方产生的。**
【代码】
#include
using namespace std;
#define db double
#define M 300005
db ans,sum;
int a[M];
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=n;i>1;i--){
sum+=a[i];
db Z=1.0*a[i]/sum;
ans=max(ans,max(ans+Z,2-Z));
}
printf("%.8f\n",ans);
return 0;
}
【分析】
这题十分的奇怪,因为纯暴力可以有58分。这可是最后一道题啊。
关于字符串的题目,博主现在只有这几种方法:Hash、Trie树。因为我还没有对字符串继续深入学。
所以除了暴力,我还想到了Hash,可是这样复杂度就是 O(nm) ,这样应该会超时。但是 很奇怪 过掉了。(加一些奇奇怪怪的优化 跑的居然比正解的AC自动机、后缀自动机还快)。
但我觉得不能超过正解,所以我还是将原始的代码贴出来。
【代码】
#include
#define usi unsigned int
#define ll long long
using namespace std;
#define M 305005
#define P 233
char a[M],b[5005];
usi num[M],F[M];
int n,m,ans;
int mk[M];
ll cnt;
int main(){
scanf("%d %s %d",&n,a+1,&m);
F[0]=1;
for(int i=1;i<=n;i++)F[i]=F[i-1]*P;
for(int i=1;i<=n;i++)num[i]=num[i-1]*P+a[i];
while(m--){
scanf("%s",b+1);
int len=strlen(b+1);
usi res=0;
for(int i=1;i<=len;i++)res=res*P+b[i];
int w=n-len+1;
for(int i=1;i<=w;i++){
if(res==num[i+len-1]-num[i-1]*F[len])mk[i]++,mk[i+len]--;
}
}
for(int i=1;i<=n;i++)cnt+=mk[i],ans+=(!cnt);
printf("%d\n",ans);
return 0;
}
这场比赛做的还是比较顺利的。两题1A,两题较快A掉,还有最后两题,花了不少时间。而且F题被一个学弟给血虐了……我感觉这其实暴露了一些问题:不是说他们天赋异禀,而是说他们比我们更踏实,不在意那些所谓的题数,而是把事情做精。虽然比他们的年龄大,但是还是要向他们学习。