left 1 Codeforces Round 920 (Div. 3)

题目链接

D.

贪心思路初步是对的
最大配最小
但是,最小也可能配最小
比如 999 1000
1 2
这个例子,就是最小配最小
还是极端到极端的问题,两个极端都要考虑

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+10;

void solve() {
    int n,m;cin>>n>>m;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a.begin()+1,a.end());
    vector<int>b(m+1);
    for(int i=1;i<=m;i++)cin>>b[i];
    sort(b.begin()+1,b.end());
    int d=0;
    int l1=1,r1=n,l2=1,r2=m;
    while(l1<=r1){
        int l=abs(a[l1]-b[r2]);
        int r=abs(a[r1]-b[l2]);
        if(l>r){
            d+=l;
            l1++;
            r2--;
        }else {
            d+=r;
            l2++;
            r1--;
        }
    }
    cout<<d<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int left=1;
    cin>>left;
    while(left--){
        solve();
    }
}

E.

A和B每走一步横坐标一定变化
所以若A的横坐标大等于B的,那么一定平局
A先手,我们可以知道A与B的横坐标之差如果是奇数,是A吃B。
A走 差/2+1,B走 差/2步
走完后A、B一定在同一行
那么再判在不在同一列
最优,一定是往一个一个方向走(由走路特点决定)
注意棋盘有边界,到边界则只能走竖直方向
判吃方横向位置能否追上被吃方即可

debug:
我算了A和B向左向右最远走了多少,然后看差值是否为0,即是否相遇
这样若棋盘够长,吃方可能比被吃方走得多
所以应该是判吃方在两个方向上能否比被吃方走得更远

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+10;

void solve() {
    int h,w,xa,ya,xb,yb;cin>>h>>w>>xa>>ya>>xb>>yb;
    if(xa>=xb){
        cout<<"Draw"<<'\n';
        return ;
    }
    int hc=abs(xa-xb);
    if(hc&1){
        int step=hc/2;
        int b1=min(w,yb+step),b2=max(1ll,yb-step),a1=min(w,ya+step+1),a2=max(1ll,ya-step-1);
        //int c=min({abs(b1-a1),abs(b1-a2),abs(b2-a1),abs(b2-a2)});
        if(a2<=b2&&a1>=b1) {
            cout << "Alice" << '\n';
            return;
        }else {
            cout << "Draw" << '\n';
            return;
        }
    }else{
        int step=hc/2;
        int b1=min(w,yb+step),b2=max(1ll,yb-step),a1=min(w,ya+step),a2=max(1ll,ya-step);
       // int c=min({abs(b1-a1),abs(b1-a2),abs(b2-a1),abs(b2-a2)});
        if(b2<=a2&&b1>=a1) {
            cout << "Bob" << '\n';
            return;
        }else {
            cout << "Draw" << '\n';
            return;
        }
    }
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int left=1;
    cin>>left;
    while(left--){
        solve();
    }
}

F.

根号分治
一共有n个数,每次最多选择n/d个数
那么若d很大,则时间复杂度并不高
若d很小,时间复杂度很高
所以考虑根号分治
d大等于根号n时,直接暴力计算
d小于等于根号时,预处理两个后缀和

注意,普通的后缀和每个数只计数了一次
我们这里有权值,所以对于后缀和再做一次后缀和
那么越靠后的数,被加的次数越多,权值就体现出来了
以下是具体的例子:
原: 1 1 1
前缀和: 3 2 1
前缀和 6 3 1
6的组成:第一个11,第二个12,第三个1*3

d2[i][d]以i为第一个且i的权值为1,i+d为第2个,且权值为2...
i+d及i+d之后的,有公共权值为d,然后是1,2,3...的权值

附图:

代码:

#include<bits/stdc++.h>
using namespace std;
long long t,n,q,a[100010],d3[100010][320],d2[100010][320];
signed main(){
    cin>>t;
    for(int l=1;l<=t;l++){
        cin>>n>>q;
        int sq=sqrt(n);
        for(int i=1;i<=n+sq;i++) for(int j=1;j<=319;j++) d3[i][j]=0,d2[i][j]=0;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=n;i>=1;i--) for(int j=1;j<=sq;j++) d3[i][j]=d3[i+j][j]+a[i];
        for(int i=n;i>=1;i--) for(int j=1;j<=sq;j++) d2[i][j]=d2[i+j][j]+d3[i][j];
        for(int j=1;j<=q;j++){
            int s,d,k;
            cin>>s>>d>>k;
            if(d>=sq){
                long long ans=0;
                for(int i=1;i<=k;i++) ans+=a[s+(i-1)*d]*i;
                cout<<ans<<" ";
            }
            else cout<<d2[s][d]-d2[s+d*k][d]-k*d3[s+d*k][d]<<endl;
        }
        cout<<endl;
    }
    return 0;
}

G.

把这个奇形怪状的范围看成仅由行或仅由列组成即可
计算时间复杂大的重要性
O(nmsqrt)
行少就枚举行,列少就枚举列
这里用的二维前缀和
对于每个点枚举4种情况

#include <bits/stdc++.h>
using namespace std;

string g[100010];
vector<int> pre[100010];
int gt(int x1,int y1,int x2,int y2)
{
    return pre[x2][y2]-(x1==0?0:pre[x1-1][y2])-(y1==0?0:pre[x2][y1-1])+(x1==0||y1==0?0:pre[x1-1][y1-1]);
}
void solve() {
    int n,m,k;
    cin>>n>>m>>k;
    for(int i = 0;i<n;i++) {cin>>g[i];pre[i].resize(m);}
    pre[0][0] = (g[0][0]=='#');
    for(int i = 0;i<n;i++)
    {
        for(int j = 0;j<m;j++)
        {
            if(i==0&&j==0) continue;
            if(i==0) pre[i][j] = pre[i][j-1]+(g[i][j]=='#');
            else if(j==0) pre[i][j] = pre[i-1][j]+(g[i][j]=='#');
            else pre[i][j] = pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+(g[i][j]=='#');
        }
    }
    int ans = 0;
    if(n<m) //暴力枚举行.
    {
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                //枚举四种方式
                int sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int row = i-l,col = min(j+k-l,m-1);
                    if(row<0) break;
                    sum+=gt(row,j,row,col);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int row = i+l,col = min(j+k-l,m-1);
                    if(row>=n) break;
                    sum+=gt(row,j,row,col);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int row = i+l,col = max(j-k+l,0);
                    if(row>=n) break;
                    sum+=gt(row,col,row,j);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int row = i-l,col = max(j-k+l,0);
                    if(row<0) break;
                    sum+=gt(row,col,row,j);
                }
                ans = max(ans,sum);
            }
        }
    } else //暴力枚举列.
    {
        for(int i = 0;i<n;i++)
        {
            for(int j = 0;j<m;j++)
            {
                //枚举四种方式
                int sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int col = j+l,row = max(0,i-k+l);
                    if(col>=m) break;
                    sum+=gt(row,col,i,col);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int col = j+l,row = min(n-1,i+k-l);
                    if(col>=m) break;
                    sum+=gt(i,col,row,col);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int col = j-l,row = min(n-1,i+k-l);
                    if(col<0) break;
                    sum+=gt(i,col,row,col);
                }
                ans = max(ans,sum);
                sum = 0;
                for(int l = 0;l<=k;l++)
                {
                    int col = j-l,row = max(0,i-k+l);
                    if(col<0) break;
                    sum+=gt(row,col,i,col);
                }
                ans = max(ans,sum);
            }
        }
    }
    cout<<ans<<'\n';
}

int main(){
    int left;
    cin>>left;
    while(left--){
        solve();
    }
    return 0;
}
posted @ 2024-01-30 21:54  WW爆米花  阅读(7)  评论(0编辑  收藏  举报