周报4

补题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的比赛也有在打,赛后也会补题。虽然说进步速度有点慢,但是也比原地踏步好。利用好最后一点假期,再补一点点算法。

posted @ 2024-02-24 21:04  osir  阅读(5)  评论(0编辑  收藏  举报