Loading

ABC269

D

Content

给你若干个点和相邻点的定义,问你图中有几个连通块。

Sol

连通用并查集维护,就是这里的相邻有点怪。

Code
#include<bits/stdc++.h>
using namespace std;
const int _=1005;
int n;
int a[_],b[_];
int ff[_];
int find(int x){return ff[x]==x?x:ff[x]=find(ff[x]);}
bool check(int x1,int y1,int x2,int y2){
    if(x1<x2) swap(x1,x2),swap(y1,y2);
    else if(x1==x2&&y1<y2) swap(y1,y2);
    if(abs(x1-x2)+abs(y1-y2)<=2&&0<=x1-x2&&x1-x2<2&&0<=y1-y2&&y1-y2<2) return 1;
    return 0;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i) cin>>a[i]>>b[i],ff[i]=i;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            if(i==j) continue;
            if(check(a[i],b[i],a[j],b[j])){
                // cout<<i<<' '<<j<<endl;
                int x=find(i),y=find(j);
                if(x==y) continue;
                ff[x]=y;
            }
        }
    }
    int ans=0;
    for(int i=1;i<=n;++i) if(ff[i]==i) ++ans;
    cout<<ans<<endl;
    return 0;
}

E

Content

交互题,有一个 \(n\times n\) 的棋盘,上面已经摆了 \(n-1\) 个象棋的车(即每行每列都至多有一个车),你每次能询问一个矩形区域里面有多少车,问再摆一个车能放在哪个格子里。

Sol

行和列是独立的,考虑先做行,相当于是找一个唯一的权是0的行,二分一个 \(mid\),询问行列范围 \(1\sim mid,1\sim n\),找到第一个结果为 \(mid-1\) 的行即为答案。列做法同理。

Code
#include<bits/stdc++.h>
using namespace std;
int n;
int ask(int l,int r,int x,int y){
    cout<<"? "<<l<<' '<<r<<' '<<x<<' '<<y<<endl;
    int t; cin>>t;
    return t;
}
int main(){
    cin>>n;
    int l=1,r=n,ansl=-1;
    while(l<=r){
        int mid=l+r>>1;
        if(ask(1,mid,1,n)<mid) ansl=mid,r=mid-1;
        else l=mid+1;
    }
    int ansr=-1;
    l=1,r=n;
    while(l<=r){
        int mid=l+r>>1;
        if(ask(1,n,1,mid)<mid) ansr=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<"! "<<ansl<<' '<<ansr;
    return 0;
}

F

Content

\(n\times m\) 的网格,每个格子上的数为 \((i-1)\times m+j\),当 \(i+j\) 为奇数时,格子上的数会变为 \(0\),询问一个矩形,问这个矩形包含元素的和。

Sol

分类讨论:

  • \(i\) 为奇,\(j\) 为偶
  • \(i\) 为偶,\(j\) 为奇

推式子 \(\sum\limits_i\sum\limits_j m(i-1)+j = \sum\limits_i(cnt_j\cdot m(i-1)+\sum\limits_j j)=(cnt_j\cdot m\sum\limits_i i) + (cnt_i\sum\limits_j j)\)

\(cnt_i\) 表示 \(i\) 有多少项,\(j\) 同理。

然后直接等差数列求和即可。

Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353,i2=499122177;
int n,m,q;
signed main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n>>m>>q;
    while(q--){
        int a,b,c,d; cin>>a>>b>>c>>d;
        int x,y,cnt1,D,DD,cnt2,res=0;
        //case 1
        if(a%2==0) x=a; else x=a-1;
        if(b%2==0) y=b-2; else y=b-1;
        if(x<=y) cnt1=(x+y)*((y-x)*i2%mod+1)%mod*i2%mod,D=(y-x)*i2%mod+1; else cnt1=D=0;
        if(c%2==0) x=c+1; else x=c;
        if(d%2==0) y=d-1; else y=d;
        if(x<=y) cnt2=(x+y)*((y-x)*i2%mod+1)%mod*i2%mod,DD=(y-x)*i2%mod+1; else cnt2=DD=0;
        res=(res+(cnt1*m%mod*DD%mod+cnt2*D%mod)%mod)%mod;
        //case 2
        if(a%2==0) x=a-1; else x=a;
        if(b%2==0) y=b-1; else y=b-2;
        if(x<=y) cnt1=(x+y)*((y-x)*i2%mod+1)%mod*i2%mod,D=(y-x)*i2%mod+1; else cnt1=D=0;
        if(c%2==0) x=c; else x=c+1;
        if(d%2==0) y=d; else y=d-1;
        if(x<=y) cnt2=(x+y)*((y-x)*i2%mod+1)%mod*i2%mod,DD=(y-x)*i2%mod+1; else cnt2=DD=0;
        res=(res+(cnt1*m%mod*DD%mod+cnt2*D%mod)%mod)%mod;
        cout<<res<<endl;
    }
    return 0;
}

G

Content

\(n\) 张正反两面写有数字的卡片,初始时全是正面,正反两面数字之和 \(M\le 2e5\),你可以翻转这 \(n\) 张卡片的任意一张。对于每个 \(k \in [0,m]\),问当前朝上的数字之和等于 \(k\) 时的最少翻转数,不能表示就为 \(-1\)

Sol

每次翻转的增量为 \(-a_i+b_i\),先考虑增量为正数情况,因为 \(\sum -a_i+b_i \le M\) 所以不同的增量个数至多 \(\sqrt M\) 个,因为等差数列显然为最有情况,有求和公式:\(\sqrt M (\sqrt M +1)/2 = M\),增量为负类似,全部乘个 \(-1\) 就是正数了。所以不同的增量个数为 \(2 \sqrt M\)

所以我们处理出不同的增量这个增量出现的个数,相当于是选若干个物品使得体积为 \(k\),多重背包问题,优化一下就行了。这里二进制拆分法要快于单调队列法,因为可以证明当 \(\sum a_i\le n\) 时,\(\sum a_i\log a_i\)\(O(n\sqrt n)\) 级别的(证明可以看官方题解)。

二进制拆分
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
const int _=4e5+5,N=2e5;
int n,m;
int a[_],b[_],f[_],S;
gp_hash_table<int,int> M;
vector<pair<int,int>> V;
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i) cin>>a[i]>>b[i],++M[b[i]-a[i]],S+=a[i];
    for(auto x:M){
        int i;
        // cout<<x.first<<' '<<x.second<<endl;
        for(i=1;i<=x.second;i<<=1) V.push_back({i*x.first,i});//,cout<<i<<' ';
        V.pop_back();
        V.push_back({(x.second-(i>>1)+1)*x.first,x.second-(i>>1)+1});
        // cout<<x.second-(i>>1)+1<<endl;
    }
    memset(f,0x3f,sizeof f);
    f[S+N]=0;
    for(auto xx:V){
        int x=xx.first,y=xx.second;
        if(x>0){
            for(int j=m;j>=-m+x;--j){
                f[j+N]=min(f[j+N],f[j-x+N]+y);
            }
        }
        else{
            for(int j=-m;j-x<=m;++j){
                f[j+N]=min(f[j+N],f[j-x+N]+y);
            }
        }
    }
    for(int i=0;i<=m;++i){
        if(f[i+N]==0x3f3f3f3f) cout<<-1<<endl;
        else cout<<f[i+N]<<endl;
    }
    return 0;
}
单调队列
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace __gnu_pbds;
using namespace std;
const int _=4e5+5,N=2e5;
int n,m;
int a[_],b[_],f[_],q[_][2],l,r,S;
gp_hash_table<int,int> M;
vector<pair<int,int>> V;
int main(){
    ios::sync_with_stdio(0); cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i) cin>>a[i]>>b[i],++M[b[i]-a[i]],S+=a[i];
    memset(f,0x3f,sizeof f);
    f[S+N]=0;
    for(auto xx:M){
        int x=xx.first,y=xx.second;
        if(x>0){
            for(int j=0;j<x;++j){
                l=1,r=0;
                q[1][0]=-114514191;
                for(int k=0;k*x+j<=m+N;++k){
                    int b=f[k*x+j]-k;
                    while(l<=r&&q[r][0]>b) --r;
                    q[++r][0]=b,q[r][1]=k;
                    while(q[l][1]+y<k) ++l;
                    f[k*x+j]=q[l][0]+k;
                }
            }
        }
        else{
            for(int j=0;j>x;--j){
                l=1,r=0;
                q[1][0]=-114514191;
                for(int k=0;m+N+k*x+j>=0;++k){
                    int b=f[m+N+k*x+j]-k;
                    while(l<=r&&q[r][0]>b) --r;
                    q[++r][0]=b,q[r][1]=k;
                    while(q[l][1]+y<k) ++l;
                    f[m+N+k*x+j]=q[l][0]+k;
                }
            }
        }
    }
    for(int i=0;i<=m;++i){
        if(f[i+N]==0x3f3f3f3f) cout<<-1<<endl;
        else cout<<f[i+N]<<endl;
    }
    return 0;
}

更简单的场了,F 以前都是送的,加了350真好。

posted @ 2022-09-18 10:59  Quick_Kk  阅读(70)  评论(0编辑  收藏  举报