10.4 模拟赛

前言

业精于勤荒于嬉,行成于思毁于随

正文(模拟赛)

卦象:凶

感受:没什么感受,半个小时码完 T1,三个小时码完 T2,剩下半个小时码完 T3 的暴力,获得 220pts,并没有挂分

T1

小分讨题

点击查看代码
#include<bits/stdc++.h>
using namespace std;
string s;int n;
inline int cal(string s){
    if(s[0]==s[1]&&s[1]==s[2])return 1;
    if(s[0]!=s[1]&&s[1]!=s[2]&&s[2]!=s[0])return 3;
    return 2;
}
inline void solve(){
    cin>>s>>n;
    if(n==0){cout<<1<<'\n';return;}
    int cnt=cal(s);
    if(cnt==1)cout<<1<<'\n';
    else if(cnt==2)cout<<(n==1?7:8)<<'\n';
    else if(cnt==3)cout<<(n==1?24:27)<<'\n';
    return;
}
int main(){
    freopen("device.in","r",stdin);
    freopen("device.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    int T;cin>>T;while(T--)solve();
    return 0;
}

T2

大分讨题

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4,V=20091023;
int a[N],b[N],d[N],q[N],t[N],tmp[N],c[N];bool vis[N];
inline void check(){
    int d1=V,d2=V;bool flag=true;
    for(int i=1;i<=3;i++){
        if(vis[i])continue;
        if(b[i])flag=false;
        if(d1==V)d1=b[i]-a[i];
        else d2=b[i]-a[i];
    }
    if(flag){cout<<1<<'\n';return;}
    if(d1==d2){cout<<1<<'\n';return;}
    int q1=V,q2=V;
    for(int i=1;i<=3;i++){
        if(vis[i])continue;
        if(a[i]==0||b[i]%a[i]!=0){cout<<2<<'\n';return;}
        if(q1==V)q1=b[i]/a[i];
        else q2=b[i]/a[i];
    }
    cout<<((q1==q2&&q1!=V)?1:2)<<'\n';
    return;
}
inline bool work1(int x,int y,int z){
    memcpy(c,a,sizeof(c));
    c[x]+=d[y],c[y]+=d[y];
    if(q[z]==V)return false;
    c[x]*=q[z],c[z]*=q[z];
    for(int i=1;i<=3;i++)
        if(c[i]!=b[i])return false;
    return true;
}
inline bool work2(int x,int y,int z){
    memcpy(c,a,sizeof(c));
    if(q[y]==V)return false;
    c[x]*=q[y],c[y]*=q[y];
    c[x]+=d[z],c[z]+=d[z];
    for(int i=1;i<=3;i++)
        if(c[i]!=b[i])return false;
    return true;
}
inline bool work3(int x,int y,int z){
    memcpy(c,b,sizeof(c));
    if(q[z]==V||q[z]==0)return false;
    if(c[x]%q[z]!=0||c[y]%q[z]!=0)return false;
    c[x]/=q[z],c[y]/=q[z];
    return c[x]-a[x]==c[y]-a[y];
}
inline bool work4(int x,int y,int z){
    memcpy(c,b,sizeof(c));
    c[x]-=d[z],c[y]-=d[z];
    if(c[x]==0&&c[y]==0)return true;
    if(a[x]==0){
        if(c[x]!=0)return false;
        if(a[y]==0)return false;
        return c[y]%a[y]==0;
    }
    if(a[y]==0){
        if(c[y]!=0)return false;
        if(a[x]==0)return false;
        return c[x]%a[x]==0;
    }
    if(c[x]%a[x]!=0||c[y]%a[y]!=0)return false;
    return c[x]/a[x]==c[y]/a[y];
}
inline bool work5(int x,int y,int z){
    memcpy(c,a,sizeof(c));
    for(int i=1;i<=3;i++)c[i]+=d[z];
    if(b[x]==0&&b[y]==0)return true;
    if(c[x]==0){
        if(b[x]!=0)return false;
        if(c[y]==0)return false;
        return b[y]%c[y]==0;
    }
    if(c[y]==0){
        if(b[y]!=0)return false;
        if(c[x]==0)return false;
        return b[x]%c[x]==0;
    }
    if(b[x]%c[x]!=0||b[y]%c[y]!=0)return false;
    return b[x]/c[x]==b[y]/c[y];
}
inline bool work6(int x,int y,int z){
    memcpy(c,a,sizeof(c));
    for(int i=1;i<=3;i++)c[i]*=q[z];
    return b[x]-c[x]==b[y]-c[y];
}
inline bool work7(){
    int p=b[2]-b[1],q=a[2]-a[1];
    if(q==0)return false;
    if(p%q!=0)return false;
    int x=p/q,y=b[1]-a[1]*x;
    return a[3]*x+y==b[3];
}
inline bool work8(){
    int p=b[2]-b[1],q=a[2]-a[1];
    if(q==0)return false;
    if(p%q!=0)return false;
    int y=p/q;
    if(y==0)return false;
    if(b[1]%y!=0)return false;
    int x=b[1]/y-a[1];
    return (a[3]+x)*y==b[3];
}
inline void solve(){
    for(int i=1;i<=3;i++)cin>>a[i];
    for(int i=1;i<=3;i++)cin>>b[i];
    for(int i=1;i<=3;i++)vis[i]=(a[i]==b[i]);
    int cnt=0;
    for(int i=1;i<=3;i++)cnt+=(vis[i]==false);
    if(cnt==0){cout<<0<<'\n';return;}
    if(cnt==1){cout<<1<<'\n';return;}
    if(cnt==2){check();return;}

    for(int i=1;i<=3;i++)d[i]=q[i]=V;
    // ans=1

    for(int i=1;i<=3;i++)d[i]=b[i]-a[i];
    if(d[1]==d[2]&&d[2]==d[3]){cout<<1<<'\n';return;}

    // for(int i=1;i<=3;i++)cerr<<d[i]<<' ';
    // cerr<<endl;

    for(int i=1;i<=3;i++)
        if(a[i]!=0&&b[i]%a[i]==0)q[i]=b[i]/a[i];

    // for(int i=1;i<=3;i++)cerr<<q[i]<<' ';
    // cerr<<endl;

    if(q[1]==q[2]&&q[2]==q[3]&&q[1]!=V){cout<<1<<'\n';return;}

    // ans=2

    memcpy(t,d,sizeof(t));sort(t+1,t+4);
    memcpy(tmp,q,sizeof(tmp));sort(tmp+1,tmp+4);
    if(t[1]==t[2]||t[2]==t[3]){cout<<2<<'\n';return;}
    if(t[1]+t[2]==t[3]||t[1]+t[3]==t[2]||t[2]+t[3]==t[1]){cout<<2<<'\n';return;}
    if((tmp[1]==tmp[2]&&tmp[1]!=V)||(tmp[2]==tmp[3]&&tmp[2]!=V)){cout<<2<<'\n';return;}
    if(tmp[1]*tmp[2]==tmp[3]||tmp[1]*tmp[3]==tmp[2]||tmp[2]*tmp[3]==tmp[1]){cout<<2<<'\n';return;}

    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work1(i,j,6-i-j)){cout<<2<<'\n';return;}
        }
    
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work2(i,j,6-i-j)){cout<<2<<'\n';return;}
        }
    
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work3(i,j,6-i-j)){cout<<2<<'\n';return;}
        }
    
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work4(i,j,6-i-j)){cout<<2<<'\n';return;}
        }
    
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work5(i,j,6-i-j)){cout<<2<<'\n';return;}
        }
    
    for(int i=1;i<=3;i++)
        for(int j=1;j<=3;j++){
            if(i==j)continue;
            if(work6(i,j,6-i-j)){cout<<2<<'\n';return;}
        }

    if(work7()){cout<<2<<'\n';return;}
    if(work8()){cout<<2<<'\n';return;}

    // ans=3
    cout<<3<<'\n';
    return;
}
inline void clr(){
    for(int i=1;i<=3;i++)d[i]=q[i]=V,vis[i]=false;
    return;
}
signed main(){
    freopen("triple.in","r",stdin);
    freopen("triple.out","w",stdout);
    int T;cin>>T;while(T--)solve(),clr();
    return 0;
}

T3

两个做法,都是单根号的

法一

考虑对 \(m\) 根号分治,设阈值为 \(B\)

如果 \(m>B\),那么最多有 \(O(\frac{n}{B})\) 次这样的询问,容易做到单次 \(O(n)\),使得复杂度 \(O(\frac{n^2}{B})\)

如果 \(m \le B\),你发现 \(x_i\) 把序列划分为不超过 \(m\) 个连续段。精妙地是,你可以把所有的询问离线下来。枚举两个连续段,统计满足 \(L_x \le l_i \le R_x ,\ L_y \le r_i \le R_y\) 的区间 \([l_i,r_i]\) 个数,显然可以二维数点,区间的奇偶性是简单的

具体地,你可以对 \(l_i\) 进行扫描线,值域上维护 \(r_i \in [L,R]\) 的个数

你可以做到 \(O(n)\) 次修改,\(O(nB)\) 次查询;所以二维数点需要分块进行时间复杂度平衡,看起来不算特别难

点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define vp vector<pii>
#define vn vector<NODE>
#define pb push_back
using namespace std;
const int N=5e5+5,B=710;
int n,Q,l[N],r[N],ans[N];vi X,p[N];
struct que{vp vec;int id;}q[N];int tol;
struct NODE{int o1,o2,o;};vn lmydj[N];
struct BLK{
    int a[N],tag[N],blk[N],L[B],R[B],siz,tot;
    inline void build(){
        siz=sqrt(n),tot=n/siz;
        for(int i=1;i<=tot;i++)L[i]=(i-1)*siz+1,R[i]=i*siz;
        if(R[tot]<n)tot++,L[tot]=R[tot-1]+1,R[tot]=n;
        for(int i=1;i<=tot;i++)for(int j=L[i];j<=R[i];j++)blk[j]=i;
        return;
    }
    inline void add(int u){
        for(int i=u;i<=R[blk[u]];i++)a[i]++;
        for(int i=blk[u]+1;i<=tot;i++)tag[i]++;
        return;
    }
    inline int ask(int u){return a[u]+tag[blk[u]];}
    inline int qry(int l,int r){return ask(r)-ask(l-1);}
}K;
int main(){
    freopen("badge.in","r",stdin);
    freopen("badge.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>Q;
    for(int i=1;i<=n;i++){
        cin>>l[i]>>r[i];
        p[r[i]].pb(l[i]);
    }
    for(int k=1;k<=Q;k++){
        int m;cin>>m;X.clear();
        for(int i=1,x;i<=m;i++)cin>>x,X.pb(x);
        sort(X.begin(),X.end());
        if(m>B){
            int a[N]={},sum[N]={};
            for(int i=0;i<m;i++)a[X[i]]++;
            for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
            int res=0;
            for(int i=1;i<=n;i++)res+=((sum[r[i]]-sum[l[i]-1])%2);
            ans[k]=res;
        }else{
            tol++;int pos=0;
            for(int i=0;i<m;i++)q[tol].vec.pb(mkp(pos,X[i])),pos=X[i];
            q[tol].vec.pb(mkp(X.back(),n+1));q[tol].id=k;
        }
    }
    for(int i=1;i<=tol;i++){
        int _size=q[i].vec.size();
        for(int j=0;j<_size;j++){
            if(q[i].vec[j].fi>0)lmydj[q[i].vec[j].fi-1].pb({i,j,-1});
            lmydj[q[i].vec[j].se-1].pb({i,j,1});
        }
    }
    K.build();
    for(int i=1;i<=n;i++){
        for(int x:p[i])K.add(x);
        for(auto x:lmydj[i]){
            int idx=x.o1,cur=x.o2;
            for(int k=0;k<cur;k++){
                if(!((cur-k)&1))continue;
                ans[q[idx].id]+=x.o*K.qry(q[idx].vec[k].fi+1,q[idx].vec[k].se);
            }
        }
    }
    for(int i=1;i<=Q;i++)cout<<ans[i]<<'\n';
    return 0;
}

法二

咱们还有传说中的莫队做法

用莫队维护一个长度为 \(Q\)\(01\) 序列,表示在某时间戳上该区间是否可以贡献答案

然后我们把修改放进来,区间扩展相当于做一个单点 flip,而答案相当于求历史和,在扩展的同时进行维护

然后就没了

代码短,但是跑不过前面的根号分治

点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define vi vector<int>
#define vp vector<pii>
#define pb push_back
#define lwbd lower_bound
#define upbd upper_bound
using namespace std;
const int N=5e5+5;
int n,Q,b[N],tol;pii a[N];
struct que{
    int l,r,blk;
    friend bool operator < (que s,que t){
        if(s.blk!=t.blk)return s.l<t.l;
        return ((s.blk&1)?s.r<t.r:s.r>t.r);
    }
}q[N];
bool vis[N];int ans[N];
inline void add(int x,int tim){
    if(vis[x])ans[x]+=tim-1,vis[x]=false;
    else ans[x]-=tim-1,vis[x]=true;
    return;
}
int main(){
    freopen("badge.in","r",stdin);
    freopen("badge.out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>Q;int siz=sqrt(n);
    for(int i=1;i<=n;i++)cin>>q[i].l>>q[i].r;
    for(int k=1;k<=Q;k++){
        int m;cin>>m;
        for(int i=1;i<=m;i++){
            int x;cin>>x;
            a[++tol]=mkp(x,k);b[tol]=x;
        }
    }
    a[++tol]=mkp(0,0);b[tol]=0;
    a[++tol]=mkp(n+1,0);b[tol]=n+1;
    sort(a+1,a+tol+1);sort(b+1,b+tol+1);
    for(int i=1;i<=n;i++)q[i].l=lwbd(b+1,b+tol+1,q[i].l)-b;
    for(int i=1;i<=n;i++)q[i].r=upbd(b+1,b+tol+1,q[i].r)-b-1;
    for(int i=1;i<=n;i++)q[i].blk=(q[i].l-1)/siz+1;
    sort(q+1,q+n+1);
    int l=1,r=0;
    for(int i=1;i<=n;i++){
        int ql=q[i].l,qr=q[i].r;
        while(r<qr)add(a[++r].se,i);
        while(l>ql)add(a[--l].se,i);
        while(r>qr)add(a[r--].se,i);
        while(l<ql)add(a[l++].se,i);
    }
    for(int i=1;i<=Q;i++)
        if(vis[i])ans[i]+=n;
    for(int i=1;i<=Q;i++)cout<<ans[i]<<'\n';
    return 0;
}

T4

两个做法,代码短与时间快

法一

约定:\(n\) 表示原题中的 \(n-1\)\(m\) 表示原题中的 \(m-1\)

感觉 \(O(nm)\) 是相当好做的,相当于直接枚举一个向量,然后在平面内漂移,统计其出现次数

我们保留其中一部分思路,即依旧考虑枚举边

\(f(i)\) 表示一条线段上有 \(x+1\) 的整点的边数,计算答案可以容斥

具体地,枚举边,枚举边外一个点,再刨除三点共线的情况。形式化地,有

\[ans=\Big (\sum_{i=1}^{n} f(i)((n+1)(m+1)+2)i \Big) - \Big( \sum_{i=1}^{n} f(i)(i+1)2i \Big) \]

第一项是这样的,除了 \(+2\) 都是好理解的,那个 \(+2\) 是当第三个点漂移到与当前枚举边的两个端点时,我们少计算一次整点的贡献

而后面的部分也很简单,三点共线本质就是最长线段包含整点个数 \(\times 2\)

当然,上述式子可以化简,写成下面的形式

\[ans=\sum_{i=1}^{n} f(i) ((n+1)(m+1)-2i)i \]

问题转化为如何快速求 \(f(i)\)

可以发现,整点个数恰为 \(i+1\) 的线段需要满足 \(\gcd(\Delta x , \Delta y)=i\),所以不妨枚举 \(i\) 的倍数 \(x\),求 \(g(i)=\sum \limits _{i \mid x} f(x)\)

显然求 \(g\) 可以差分求出 \(f\),并且 \(g\) 是好算的,考虑其几何意义,有:

\[g(k)=\sum_{i=1}^{\lfloor n/k \rfloor}(n-ik+1)(m+1) + \sum_{i=1}^{\lfloor m/k \rfloor}(m-ik+1)(n+1) + 2\sum_{i=1}^{\lfloor n/k \rfloor}(n-ik+1)\sum_{i=1}^{\lfloor m/k \rfloor}(m-ik+1) \]

哦,然后直接维护维护完了

点击查看代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N=1e6+5;
int n,m,f[N];
inline int read(){
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return x;
}
inline void write(int x){
    if(x>9)write(x/10);
    putchar(x%10+'0');
    return;
}
inline void solve(){
    n=read(),m=read();n--,m--;
    int ans=0;
    memset(f,0,sizeof(f));
    for(int i=max(n,m);i>=1;i--){
        int sum1=0,sum2=0;
        for(int j=1;j<=n/i;j++)sum1+=(n-i*j+1);
        for(int j=1;j<=m/i;j++)sum2+=(m-i*j+1);
        f[i]=2*sum1*sum2;
        for(int j=1;j<=n/i;j++)f[i]+=(n-i*j+1)*(m+1);
        for(int j=1;j<=m/i;j++)f[i]+=(m-i*j+1)*(n+1);
        for(int j=2*i;j<=max(n,m);j+=i)f[i]-=f[j];
        ans+=f[i]*((n+1)*(m+1)-2*i)*i;
    }
    write(ans);puts("");
    return;
}
signed main(){
    freopen("grid.in","r",stdin);
    freopen("grid.out","w",stdout);
    int T=read();while(T--)solve();
    return 0;
}

法二

指路 HTC 大巨的题解

小结

下午效率好低……

后记

世界孤立我任它奚落

完结撒花!

posted @ 2025-10-04 20:25  sunxuhetai  阅读(12)  评论(0)    收藏  举报