补题1:秀恩爱分的快
题意:
做法:先读入照片的数据存起来,知道A,B的性别之后再遍历照片,按照异性进行cnt.有一个细节就是0和-0是无法区分的如果不用字符串读入的话。所以要字符串来存数据,后面再做判断,剩下的就简单遍历一下照片,然后relateA和relateB分开计算即可。
bool cmp(pair<double,int> a,pair<double,int> b){
if(a.first!=b.first) return a.first>b.first;
return a.second<b.second;
}
void solve(){ //补7-12秀恩爱分的快
int n,m,k;
pair<double,int> relateA[1005];
pair<double,int> relateB[1005];
cin>>n>>m;
for(int i=0;i<1000;i++){ //init
relateA[i].first=0;
relateA[i].second=i;
relateB[i].first=0;
relateB[i].second=i;
}
string x;
vector<string> vct[m];
for(int i=0;i<m;i++){
cin>>k;
while(k--){
cin>>x;
vct[i].emplace_back(x);
}
}
bool sexA=false,sexB=false;
string A,B;
int numa,numb;
cin>>A>>B;
if(A[0]=='-') {
sexA=true;
numa=stoi(A.substr(1,A.size()-1));
numb=stoi(B);
}
else {
sexB=true;
numb=stoi(B.substr(1,B.size()-1));
numa=stoi(A);
}
for(int i=0;i<m;i++){
if(sexA){ //A为负
if(find(vct[i].begin(),vct[i].end(),A)!=vct[i].end()){
for(auto v:vct[i]){
if(v[0]!='-'&&v!=A) relateA[stoi(v)].first+=1.0/vct[i].size();
}
}
if(find(vct[i].begin(),vct[i].end(),B)!=vct[i].end()){
for(auto v:vct[i]){
if(v[0]=='-'&&v!=B) relateB[stoi(v.substr(1,v.size()-1))].first+=1.0/vct[i].size();
}
}
}
else{ //B为负
if(find(vct[i].begin(),vct[i].end(),A)!=vct[i].end()){
for(auto v:vct[i]){
if(v[0]=='-'&&v!=A) relateA[stoi(v.substr(1,v.size()-1))].first+=1.0/vct[i].size();
}
}
if(find(vct[i].begin(),vct[i].end(),B)!=vct[i].end()){
for(auto v:vct[i]){
if(v[0]!='-'&&v!=B) relateB[stoi(v)].first+=1.0/vct[i].size();
}
}
}
}
sort(relateA,relateA+1000,cmp);
sort(relateB,relateB+1000,cmp);
bool checka=false,checkb=false;
for(int i=0;i<1000;i++){
if(relateA[i].first!=relateA[0].first) break;
if(relateA[i].second==numb) {
checka=true;
break;
}
}
for(int i=0;i<1000;i++){
if(relateB[i].first!=relateB[0].first) break;
if(relateB[i].second==numa) {
checkb=true;
break;
}
}
if(checka&&checkb) cout<<A<<" "<<B;
else{
for(int i=0;i<1000;i++){
if(relateA[i].first!=relateA[0].first) break;
if(sexA) cout<<A<<" "<<relateA[i].second<<endl;
else cout<<A<<" -"<<relateA[i].second<<endl;
}
for(int i=0;i<1000;i++){
if(relateB[i].first!=relateB[0].first) break;
if(sexB) cout<<B<<" "<<relateB[i].second<<endl;
else cout<<B<<" -"<<relateB[i].second<<endl;
}
}
}
补题2:数三角形(eazy)
题意:
做法:暴力枚举*当做顶点即可,但是也不能太暴力,需要加上前缀和来判断底边那个直线,不然会一个底会被重复数很多次,非常浪费时间。写的时候也想了一下加个前缀和,但是感觉不需要,就没加前缀和了。实际上不加前缀会浪费很多时间。用了前缀和之后,每次判断底边是否全都是*,o(1)即可,否则又要遍历底边。
void solve(){ //数三角形(eazy)--遍历,遇到*就往下找-TLE -->用递归?--也是TLE ;;;遇到*就往下找-加一个前缀和
//加一个前缀和来判断直线!!!不然会多次跑重复的,非常浪费时间
int n,m,ans=0; //o(n^4)会TLE, o(n^3)可以
cin>>n>>m;
string maze[505];
int pre[505][505];
for(int i=0;i<n;i++) cin>>maze[i];
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(j==0){
if(maze[i][j]=='*') pre[i][j]=1;
else pre[i][j]=0;
}
else{
if(maze[i][j]=='*') pre[i][j]=pre[i][j-1]+1;
else pre[i][j]=pre[i][j-1];
}
}
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
if(maze[i][j]=='*'){
int l,r;
for(int f=1;1;f++){
if(i+f<n&&j-f>=0&&j+f<m&&maze[i+f][j-f]=='*'&&maze[i+f][j+f]=='*') {
l=j-f,r=j+f;
if(l==0&&pre[i+f][r]==r+1) ans++; //pre[i+f][r]==r --> ==r+1 !!!下标从0开始的
else if(pre[i+f][r]-pre[i+f][l-1]==r-l+1) ans++;
}
else break;
}
}
}
}
cout<<ans<<endl;
}
补题3:漂亮数组
题意:
做法:记录一个前缀和,每次计算出当前位置前缀和时,把该位置前缀和%k,如果得出的数字是前面前缀和出现过的数字,或刚好可以%k==0。那么那个数字的后一个位置到现在这个位置就是一个合法区间 //因为一旦出现k的倍数,取余加法是有分配律的,k的倍数取余之后为0,加上前缀和会变成之前出现过的数字。再加上贪心即可得出答案。
int pre[200005];
void solve(){ //E漂亮数组--一眼感觉双指针
// 当l,r满足时,应该立即切断。如果还往里面加东西,肯定就不是k的倍数了;除非又加了一次k的倍数进去,那么这样就会少了一个答案。(贪心)
//NoNoNo--这个区间可能只有中间一段是可以的,前面和后面要舍弃。
//那怎么知道前面应该舍弃多少?dp??--不是dp
//做法:记录一个前缀和,每次计算出当前位置前缀和时,把该位置前缀和%k,如果得出的数字是前面前缀和出现过的数字,那么那个数字的后一个位置到现在这个位置就是一个合法区间
//因为一旦出现k的倍数,取余加法是有分配律的,k的倍数取余之后为0,加上前缀和会变成之前出现过的数字。再加上贪心即可
int n,x,k,ans=0;
cin>>n>>k;
unordered_map<int,int> mp;
for(int i=1;i<=n;i++){
cin>>x;
pre[i]=pre[i-1]+x;
if(mp[pre[i]%k]||pre[i]%k==0) {
ans++;
pre[i]=0; //不影响后面的,前面的数据已经没用
mp.clear();
}
else mp[pre[i]%k]=1;
}
cout<<ans<<endl;
}
补题4:E-FinalCountdown
题意:
做法:这题实质上是要数每一位会发生多少次变化的总和。只要意识到这点,那么答案显而易见。例如12345,个位会变化12345次,十位会变化1234次,百位会变化123次,千位会变化12次,万位会变化1次。全部加起来即是答案。自己被样例的解释影响太深了,按照样例的想法计算,一直算不对..这题怎么存这个答案也是要考虑的问题,最大有4e5位,还要进行相加。
注意这题的数字很大,都是字符串输入的,所以用高精度来存数字。--no,,数字最大有4e5位,数组即使可以开这么大,按照这样计算会是o(n^2),高精度会超时。
用前缀和记录各位相加的结果。最后再遍历一遍ans数组,处理进位,最后把每一个数字输出,实际上类似高精度--个位与个位运算,十位与十位运算....
//void high_precision_sum(int sum[],string str){
// int g=0,idx=str.size()-1;
// for(int i=400004;i>=0;i--){
// if(idx>=0){
// sum[i]+=str[idx]-'0';
// sum[i]+=g;
// g=sum[i]/10;
// sum[i]%=10;
// idx--;
// }
// else if(g){
// sum[i]+=g;
// g=sum[i]/10;
// sum[i]%=10;
// }
// }
//}
int pre[400005]={0};
void solve(){ //E--思维--计算结果也巧妙,个位与个位,十位与十位...进行运算(小学加法),最后处理进位。!!!!
//这题有点可以。。。。。。
int n;
cin>>n;
string str;
cin>>str;
//int sum[400005]={0};
for(int i=0;i<str.size();i++) pre[i+1]=pre[i]+str[i]-'0'; //腾出第一个位置,方便进位!!!!
// (会不会最后进位超过一位?...)--不管,g单独输出,如果不为0的话,就算是超过一位的进位也能直接输出,不用存到数组里面。这样数组的位置一定是足够的。
int g=0;
for(int i=str.size();i>0;i--){
pre[i]+=g;
g=pre[i]/10;
pre[i]%=10;
}
if(g!=0) cout<<g;
bool check=false;
for(int i=1;i<=str.size();i++) {
if(g!=0||pre[i]!=0||check) {
cout<<pre[i];
check=true;
}
}
cout<<endl;
//高精度会TLE--非常非常TLE。。o(4e5*4e5)
// for(int i=0;i<str.size();i++) high_precision_sum(sum,str.substr(0,str.size()-i));
// bool check=false;
// for(int i=0;i<400005;i++){
// if(sum[i]!=0||check){
// cout<<sum[i];
// check=true;
// }
// }
// cout<<endl;
}
补题5:C-Lexicographically Largest
题意:
1<=n<=3e5,1<=ai<=1e9.
做法:代码注释
int arr[300005];
void solve(){ //C--单纯猜测过:单独的直接拿,有重复的就逐个递减,避免重复,也不用管他们的位置。但是这是单纯猜测,不敢写,但是貌似的确是这样。
// 7 6 10 10 3 2 1 --> 8 8 13 (14) 8 8 8 --> 8 8 (13) 7 7 7 --> 8 8 (6) 6 6 --> 8 8 (5) 5 --> 8 8 (4) --> (8) 8 --> 7
//ans:14 13 8 7 6 5 4
//7 6 5 4 3 2 1 --> 8 8 8 8 8 8 8
//ans:8 7 6 5 4 3 2 1
//因为答案是S从大到小输出,所以插入S是顺序是无关的,我们要保证插入的数字尽可能大,而且不要重复。
//可以证明n个数字中,就算ai+i有相同的,也能取满n个数字,不会有重复的数字。
//从上面举的例子可以发现,就算多个相同的数字中间插入了不同的数字,还是能取到n个不同的数字,而且大小是尽可能大的。
//如果全部ai+i都不相同,那么很明显,一直取最右边一个即可。而像上面如果出现有相同的(即使是很多个),总是能把他们取成逐个递减的。而中间具体是证怎么的,则不用管,反正是可以。
//举个例子,假如有7个ai+i的值都是9,我们总是先取最大的ai+i,期间一边也可以取减少过的原本是9的值,让其所有原本的9,取出来是9,8,7,6,5,4,3.
int n;
cin>>n;
for(int i=1;i<=n;i++) {
cin>>arr[i];
arr[i]+=i;
}
sort(arr+1,arr+n+1,greater<int>());
int minn=arr[1]+1;
for(int i=1;i<=n;i++){
if(arr[i]>=minn){
cout<<minn-1<<" ";
minn--;
}
else{
cout<<arr[i]<<" ";
minn=arr[i];
}
}
cout<<endl;
}
总结:这周打的牛客训练营感觉还差点,几题感觉快要写出来的题,赛时总是写不出来,赛后看一眼题解马上又顿悟。现在可能再加把劲,又可以提升一点点了。cf的比赛也有在打,赛后也会补题。虽然说进步速度有点慢,但是也比原地踏步好。利用好最后一点假期,再补一点点算法。