1. 程式人生 > 其它 >【考試總結】2022-03-31

【考試總結】2022-03-31

\(\{a\}\) 排序

考慮計算強制所有點都在環上的連邊方案數,設 \(f_{i,j,k}\) 表示剩下 \(j\) 個單點和 \(k\) 條鏈時的方案數,轉移討論 合併兩個單點/合併單點及鏈/合併兩條鏈 三種情況即可

由於鏈可以雙向使用,那麼每次新增新鏈視作兩個,而合併兩個鏈的係數為 \(k(k-2)\)

其實這裡就能發現並沒有必要將鏈單獨表示成一維,根據實際含義視作鏈尾的點即可壓成兩維

那麼不強制所有點在環上的方案數就是在進行 \(a_i-a_{i-1}\) 個點的新增時列舉新增點數 \(d\) 進行對應轉移到 \(f_{i,j+d}\) 同時要附加 \(\binom{a_i-a_{i-1}}d\)

的係數即可

如果 \(j+d=1\) 時再產生合併就得到了一個大環,此時進行更新答案

注意這種計算方式將二元環計入了答案,需要減去;同時每個環被正反計算了兩次,要除 \(2\)

Code Display
const int N=5010;
int dp[2][N],fac[N],ifac[N],n,a[N],ans;
inline int C(int n,int m){return mul(fac[n],mul(ifac[m],ifac[n-m]));}
signed main(){
    freopen("ring.in","r",stdin); freopen("ring.out","w",stdout);
    n=read(); fac[0]=1;
    rep(i,1,n) a[i]=read(),fac[i]=mul(fac[i-1],i);
    ifac[n]=ksm(fac[n],mod-2);
    Down(i,n,1) ifac[i-1]=mul(ifac[i],i);
    sort(a+1,a+n+1);
    int cur=0;
    dp[cur][0]=1;
    for(int i=1;i<=n;++i){
        for(int j=0;j<=n;++j) if(dp[cur][j]){
            dp[cur][j]%=mod;
            int num=a[i]-a[i-1];
            for(int k=0;k<=num;++k){
                int coef=C(num,k);
                dp[cur^1][j+k]+=mul(coef,dp[cur][j]);
                if(j+k>=2) dp[cur^1][j+k-1]+=(j+k)*(j+k-1)*mul(coef,dp[cur][j])%mod;
                else if(j+k==1) ckadd(ans,mul(dp[cur][j],coef));
            }
            dp[cur][j]=0;
        }
        cur^=1;
    }
    rep(i,1,n) ckdel(ans,a[i]);
    print(mul(ans,(mod+1)/2));
    return 0;
}

進行簡單關於等差數列的和式變換可以發現滿足條件的 \((a,b,c,k)\) 需要滿足(設 \(d\) 為公差) \(-k=(a-3d)(a+d)\)

那麼設 \(x=3d-a,y=a+d\) 那麼需要滿足 \(4|(x+y)\)\(y\ge\frac13 x\) ,討論 \(y\) 在模 \(4\) 意義下的餘數:

  • 是奇數,對應 \(k\) 是模 \(4\)\(3\) 的質數,否則分解方式為 \(0\) 或者大於 \(1\)

  • \(4\)\(0\) ,對應 \(k\)\(16\) 的倍數,這時 \(\frac k{16}\) 如果不是不為 \(1\) 的質數或者為 \(2\)

    那麼能通過任意分解質因數找到 \(y\le \frac{1}3x\) 的兩組解

  • \(4\)\(2\) ,此時 \(x\equiv 2 \mod 4\Rightarrow 4|k\) ,同上 \(\frac{k}4\) 也是 \(1\) 或者奇質數

剩下的工作是若干次 \(\min \_25\) 篩,歡迎給這份程式碼加 \(\rm hack\) 資料哦!

Code Display
const int N=2e6+10;
namespace Prime_calculator{
    int n,block,tot;
    int id1[N],id2[N],val[N],g[N];
    int pri[N],cnt,fl[N];
    inline int get_id(int x){return x<=block?id1[x]:id2[n/x];}
    inline void clear(){
        n=block=tot=cnt=0;
        memset(pri,0,sizeof(pri));
        memset(fl,0,sizeof(fl));
        memset(g,0,sizeof(g));
        memset(val,0,sizeof(val));
        memset(id1,0,sizeof(id1));
        memset(id2,0,sizeof(id2));
    }
    inline int solve(int NN){
        clear();
        block=sqrt(n=NN); 
		for(int i=2;i<=block;++i){
			if(!fl[i]) pri[++cnt]=i; 
			for(int j=1;j<=cnt&&i*pri[j]<=block;++j){
				fl[i*pri[j]]=1; 
                if(i%pri[j]==0) break;
			} 
		}
        for(int l=1,r;l<=n;l=r+1){
			val[++tot]=n/l; r=n/val[tot]; 
            g[tot]=val[tot]-1;
			if(val[tot]<=block) id1[val[tot]]=tot; 
            else id2[r]=tot;
		} 
		for(int i=1;i<=cnt;++i){
            for(int j=1;pri[i]*pri[i]<=val[j];++j){
                g[j]-=g[get_id(val[j]/pri[i])]-(i-1);
            }
        }
        return g[1];  
    }   
}
namespace Guass_counter{
    int fl[N],cnt,pri[N],n;
    int num1[N],num3[N],g1[N],g3[N],id1[N],id2[N];
    int tot,block,val[N];
    inline int get_id(int x){return x<=block?id1[x]:id2[n/x];}
    inline int solve(int NN){
        block=sqrt(n=NN);
        for(int i=2;i<=block;++i){
            if(!fl[i]){
                pri[++cnt]=i; 
                num1[cnt]=num1[cnt-1]; num3[cnt]=num3[cnt-1];
                if(i%4==3) num3[cnt]++;
                if(i%4==1) num1[cnt]++;
            }
            for(int j=1;j<=cnt&&i*pri[j]<=block;++j){
                fl[i*pri[j]]=1; 
                if(i%pri[j]==0) break;
            }
        }
        for(int l=1,r;l<=n;l=r+1){
            val[++tot]=n/l; r=n/val[tot];
            g1[tot]=(val[tot]-1)/4;
            g3[tot]=(val[tot]+1)/4;
            if(val[tot]<=block) id1[val[tot]]=tot; 
            else id2[r]=tot;
        }
        rep(i,2,cnt){
            if(pri[i]%4==1){
                for(int j=1;pri[i]*pri[i]<=val[j];++j){
                    int x=get_id(val[j]/pri[i]);
                    g1[j]-=g1[x]-num1[i-1];
                    g3[j]-=g3[x]-num3[i-1];
                }
            }else{
                for(int j=1;pri[i]*pri[i]<=val[j];++j){
                    int x=get_id(val[j]/pri[i]);
                    g1[j]-=g3[x]-num3[i-1];
                    g3[j]-=g1[x]-num1[i-1];
                }
            }
        }
        return g3[get_id(n)];
    }
}
inline bool check(int x,int y){
    if((x+y)%4) return 0;
    int d=(x+y)/4,a=y-d;
    return a>0;
}
inline bool check(int n){
    int cnt=0;
    for(int i=1;cnt<=2&&i*i<=n;++i) if(n%i==0){
        cnt+=check(i,n/i);
        if(i!=n/i) cnt+=check(n/i,i);
    } 
    return cnt==1;
}
signed main(){
    freopen("number.in","r",stdin); freopen("number.out","w",stdout);
    int n=read();
    int ans=Guass_counter::solve(n)+Prime_calculator::solve(n/4)+Prime_calculator::solve(n/16);
    print(ans); putchar('\n');
    return 0;
}

矩陣

參考 \(q\le 10\) 的部分分發現本題基礎想法應當為列舉 \(x\in[x_1,x_2]\) 並計算區間 \([y_1,y_2]\) 的最大值再統一取 \(\max\)

\(x\) 這維分治,在區間 \([L,R]\) 處理 \(L\le x_1\le mid=\frac{L+R}2<x_2\le R\) 的詢問 \(\{[x_1,x_2]\}\),此時使用線段樹維護 \(y\) 軸資訊

遞迴左側;此時滿足 \([L,mid]\) 的資訊都線上段樹上表示,倒序撤銷之並維護線段樹歷史資訊最大值,對於每個詢問在左端點 \(x_1\) 查詢 \([y_1,y_2]\) 的歷史最大值

清空線段樹歷史最大值並順序加入右半子區間資訊,在詢問右端點處再次進行歷史最大值的回答,處理完畢後遞迴右半區間

實現上有一些細節

其一是由於線段樹維護歷史最大值是將歷史設定為每次詢問一個版本而非每個 \(x\) 值進行操作結束後一次湧入,那麼加入矩形資訊需要先刪後加,對應處理左半區間的倒序撤銷操作需要先刪加再添減

其二是注意在清空歷史最大值時需要將歷史最大值設定成當前的 \(\max\) 來滿足實際含義,那麼需要先下放區間修改的懶標記來保證子區間的 \(\max\) 是正確的

Code Display
const int N=2e5+10;
int n,m,Q,ans[N];
int qa[N],qb[N],qc[N],qd[N];
vector<tuple<int,int,int> >inc[N],red[N];
struct Seg{
    #define ls p<<1
    #define rs p<<1|1
    #define lson p<<1,l,mid
    #define rson p<<1|1,mid+1,r
    int mx[N<<2],tag[N<<2],his[N<<2],mxtag[N<<2];
    bool clr[N<<2],leaf[N<<2];
    inline void build(int p,int l,int r){
        if((leaf[p]=(l==r))) return ;
        int mid=(l+r)>>1;
        build(lson); build(rson);
        return ;
    }
    inline void push_tag(int p,int v,int vmx){
        ckmax(his[p],vmx+mx[p]);
        ckmax(mxtag[p],vmx+tag[p]);
        mx[p]+=v; tag[p]+=v;
        return ;
    }
    inline void clear(int p){
        if(!leaf[p]){
            push_tag(ls,tag[p],mxtag[p]);
            push_tag(rs,tag[p],mxtag[p]);
            clr[p]=1;
        }
        his[p]=mx[p]; 
        mxtag[p]=tag[p]=0;
    }
    inline void push_up(int p){
        mx[p]=max(mx[ls],mx[rs]);
        his[p]=max(his[ls],his[rs]);
    }
    inline void push_down(int p){
        if(clr[p]){
            clear(ls); clear(rs);
            clr[p]=0;
        }
        push_tag(ls,tag[p],mxtag[p]);
        push_tag(rs,tag[p],mxtag[p]);
        mxtag[p]=tag[p]=0;
        return ;
    }
    inline void upd(int st,int ed,int v,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return push_tag(p,v,v);
        int mid=(l+r)>>1; push_down(p);
        if(st<=mid) upd(st,ed,v,lson);
        if(ed>mid) upd(st,ed,v,rson);
        return push_up(p);
    }
    inline int query(int st,int ed,int p=1,int l=1,int r=n){
        if(st<=l&&r<=ed) return his[p];
        int mid=(l+r)>>1,res=0; push_down(p);
        if(st<=mid) ckmax(res,query(st,ed,lson));
        if(ed>mid) ckmax(res,query(st,ed,rson));
        return res;
    }
}T;
inline void insert(int lin){
    for(auto t:red[lin]) T.upd(get<0>(t),get<1>(t),get<2>(t));
    for(auto t:inc[lin]) T.upd(get<0>(t),get<1>(t),get<2>(t));
}
inline void erase(int lin){
    for(auto t:inc[lin]) T.upd(get<0>(t),get<1>(t),-get<2>(t));
    for(auto t:red[lin]) T.upd(get<0>(t),get<1>(t),-get<2>(t));
}
inline void solve(int l,int r,vector<int> cur){
    if(!cur.size()){
        rep(i,l,r) insert(i);
        return ;
    }
    if(l==r){
        insert(l);
        T.clear(1);
        for(auto qid:cur) ckmax(ans[qid],T.query(qb[qid],qd[qid]));
        return ;
    }
    int mid=(l+r)>>1;
    vector<int> cro,Lef,Rig;
    for(auto qid:cur){
        if(qa[qid]<=mid&&qc[qid]>mid) cro.emplace_back(qid);
        else if(qc[qid]<=mid) Lef.emplace_back(qid);
        else Rig.emplace_back(qid);
    }
    solve(l,mid,Lef);
    T.clear(1);
    sort(cro.begin(),cro.end(),[&](const int x,const int y){return qa[x]>qa[y];});
    int indic=0,siz=cro.size();
    for(int i=mid;i>=l;--i){
        while(indic<siz&&qa[cro[indic]]==i){
            int id=cro[indic];
            ckmax(ans[id],T.query(qb[id],qd[id])),++indic;
        }
        erase(i);
    }
    for(int i=l;i<=mid;++i) insert(i);
    solve(mid+1,r,Rig);
    sort(cro.begin(),cro.end(),[&](const int x,const int y){return qc[x]<qc[y];});
    indic=0;
    for(int i=r;i>mid;--i) erase(i);
    T.clear(1);
    for(int i=mid+1;i<=r;++i){
        insert(i);
        while(indic<siz&&qc[cro[indic]]==i){
            int id=cro[indic];
            ckmax(ans[id],T.query(qb[id],qd[id])),++indic;
        }
    }
    return ;
}
signed main(){
    freopen("matrix.in","r",stdin); freopen("matrix.out","w",stdout);
    n=read(); m=read(); Q=read();
    bool one=1;
    for(int i=1;i<=m;++i){
        int a=read(),b=read(),c=read(),d=read(),v=read();
        inc[a].emplace_back(b,d,v);
        red[c+1].emplace_back(b,d,-v);
    }
    vector<int> all(Q);
    for(int i=1;i<=Q;++i){
        qa[i]=read(),qb[i]=read(); qc[i]=read(); qd[i]=read();
        all[i-1]=i;
    }
    T.build(1,1,n);
    solve(1,n,all);
    rep(i,1,Q) print(ans[i]);
    return 0;
}