N2C1 题解

T1

二分图匹配的板子题。

T2

普通莫队算法。
对于每个询问区间 \([L,R]\),长度 \(len=R-L+1\),那么从该区间任选 \(2\) 个数的方案数为: $$C_{len}^2=\frac{len(len-1)}{2}$$。
维护 \(add\) 函数和 \(del\) 函数。定义 \(cnt[x]\) 表示 \(x\) 在查询区间内出现了多少次。从该区间任选 \(2\) 个数相同的方案数为:

\[C_{cnt[x]}^2=\frac{cnt[x](cnt[x]-1)}{2} \]

题目要求输出最简分数,分子分母同除 \(\gcd\) 即可。数据比较大开 \(long \ long\)\(len\)\(0\) 要特判。

点击查看 $AC$ 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+8;
int n,m,a[N],cnt[N],bsz;
ll ans1[N],ans2[N],res;
struct node{
    int id,l,r;
    bool operator<(const node &rhs)const{
        if(l/bsz!=rhs.l/bsz)return l<rhs.l;
        if((l/bsz)&1)return r>rhs.r;
        return r<rhs.r;
    }
}q[N];
void add(int x){
    res-=cnt[x]*(cnt[x]-1)/2;
    cnt[x]++;
    res+=cnt[x]*(cnt[x]-1)/2;
}
void del(int x){
    res-=cnt[x]*(cnt[x]-1)/2;
    cnt[x]--;
    res+=cnt[x]*(cnt[x]-1)/2;
}
ll gcd(int a,int b){
    if(b==0)return a;
    else return gcd(b,a%b);
}
int main(){
    cin>>n>>m;
    bsz=sqrt(n);
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=m;i++){
        cin>>q[i].l>>q[i].r;
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(l>q[i].l)add(a[--l]);
        while(r<q[i].r)add(a[++r]);
        while(l<q[i].l)del(a[l++]);
        while(r>q[i].r)del(a[r--]);
        ll len=q[i].r-q[i].l+1;
        if(len<=1){
            ans1[q[i].id]=0;
            ans2[q[i].id]=1;
            continue;
        }
        ll tot=(len-1)*len/2;
        ll g=gcd(res,tot);
        ans1[q[i].id]=res/g;
        ans2[q[i].id]=tot/g;
    }
    for(int i=1;i<=m;i++){
        cout<<ans1[i]<<"/"<<ans2[i]<<"\n";
    }
    return 0;
}

T3

斜率优化 \(DP\)
这题需要转化。考虑将矩形按 \(w\) 从大到小排序,若后一个矩形的 \(l\) 比前一个矩形的 \(l\) 小,那么前一个矩形和后一个矩形一起打包是没有代价的。换句话说,可以把这样多余的矩形从序列中删除。这样就维护出了一个 \(w\) 从大到小,\(l\) 从小到大的序列,可以动态规划了。
定义 \(dp[i]\) 表示维护后的序列中前 \(i\) 个矩形的最小总费用,那么转移方程为:

\[dp[i]=\min_{0\leq j<i}(dp[j]+w[j+1]\times l[i]) \]

符合斜率优化的基本形式,故变形:

\[dp[j]=-l[i]\times w[j+1]+dp[i] \]

其中 \(y_j=dp[j], \ k_i=-l[i],\ x_j=w[j+1],\ b=dp[i]\)
套板子就行了。注意数据比较大开 \(long \ long\)。最后输出的是 \(dp[cnt]\),而不是 \(dp[n]\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e4+8;
int n,hd,tl,q[N];
ll dp[N];
struct node{
    int w,l;
    bool operator<(const node &rhs)const{
        if(w!=rhs.w)return w>rhs.w;
        return l>rhs.l;
    }
}a[N],tmp[N];
ll Y(int j){
    return dp[j];
}
ll K(int i){
    return -tmp[i].l;
}
ll X(int j){
    return tmp[j+1].w;
}
double slope(int j1,int j2){
    return 1.0*(Y(j2)-Y(j1))/(X(j2)-X(j1));
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i].w>>a[i].l;
    sort(a+1,a+n+1);
    int cnt=0,mxl=0;
    for(int i=1;i<=n;i++){
        if(a[i].l>mxl){
            tmp[++cnt]=a[i];
            mxl=a[i].l;
        }
    }
    hd=1,tl=0,q[++tl]=0;
    for(int i=1;i<=n;i++){
        while(hd<tl&&slope(q[hd],q[hd+1])>=K(i))hd++;
        dp[i]=Y(q[hd])-K(i)*X(q[hd]);
        while(hd<tl&&slope(q[tl-1],i)>=slope(q[tl-1],q[tl]))tl--;
        q[++tl]=i;
    }
    cout<<dp[cnt];
    return 0;
}

T4

树链剖分 \(+\) 分块。
先通过树链剖分将树上问题转化为区间问题,再分块维护两个信息的单点修改和区间查询。
思路不难,码量显著。

点击查看 $AC$ 代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+8,BLK=350;
int n,q,c[N],w[N];
int bsz,dfn2w[N],dfn2c[N];
int clk,dfn[N],fa[N],dep[N],sz[N],son[N],top[N];
vector<int> g[N];
struct block{
	int bid[N],lb[BLK],rb[BLK],sum[N][BLK],mx[N][BLK];
	void build(){
		for(int i=1;i<=n;i++){
			bid[i]=(i-1)/bsz+1;
			sum[dfn2c[i]][bid[i]]+=dfn2w[i];
			mx[dfn2c[i]][bid[i]]=max(mx[dfn2c[i]][bid[i]],dfn2w[i]);
		}
		for(int i=1;i<=bid[n];i++){
			lb[i]=rb[i-1]+1;
			rb[i]=min(i*bsz,n);
		}
	}
	void updval(int pos,int x){
		int id=bid[pos],clr=dfn2c[pos];
		sum[clr][id]-=dfn2w[pos],dfn2w[pos]=x,sum[clr][id]+=dfn2w[pos];
		mx[clr][id]=0;
		for(int i=lb[id];i<=rb[id];i++)
			if(dfn2c[i]==clr)mx[clr][id]=max(mx[clr][id],dfn2w[i]);
	}
	void updclr(int pos,int clr){
		int id=bid[pos],pc=dfn2c[pos];
		sum[pc][id]-=dfn2w[pos],dfn2c[pos]=clr,sum[clr][id]+=dfn2w[pos];
		mx[clr][id]=max(mx[clr][id],dfn2w[pos]),mx[pc][id]=0;
		for(int i=lb[id];i<=rb[id];i++)
			if(dfn2c[i]==pc)mx[pc][id]=max(mx[pc][id],dfn2w[i]);
	}
	int querysum(int l,int r,int clr){
		int ret=0,lid=bid[l],rid=bid[r];
		if(lid==rid){
			for(int i=l;i<=r;i++)ret+=(dfn2c[i]==clr?dfn2w[i]:0);
			return ret;
		}
		for(int i=l;bid[i]==lid;i++)ret+=(dfn2c[i]==clr?dfn2w[i]:0);
		for(int i=lid+1;i<=rid-1;i++)ret+=sum[clr][i];
		for(int i=r;bid[i]==rid;i--)ret+=(dfn2c[i]==clr?dfn2w[i]:0);
		return ret;
	}
	int querymx(int l,int r,int clr){
		int ret=0,lid=bid[l],rid=bid[r];
		if(lid==rid){
			for(int i=l;i<=r;i++)ret=max(ret,(dfn2c[i]==clr?dfn2w[i]:0));
			return ret;
		}
		for(int i=l;bid[i]==lid;i++)ret=max(ret,(dfn2c[i]==clr?dfn2w[i]:0));
		for(int i=lid+1;i<=rid-1;i++)ret=max(ret,mx[clr][i]);
		for(int i=r;bid[i]==rid;i--)ret=max(ret,(dfn2c[i]==clr?dfn2w[i]:0));
		return ret; 
	}
}blk;
void dfs1(int u,int f){
	fa[u]=f,dep[u]=dep[f]+1,sz[u]=1;
	for(int v:g[u]){
		if(v==f)continue;
		dfs1(v,u);
		sz[u]+=sz[v];
		if(sz[v]>sz[son[u]])son[u]=v; 
	}
}
void dfs2(int u,int tp){
	dfn[u]=++clk,top[u]=tp,dfn2w[clk]=w[u],dfn2c[clk]=c[u];
	if(son[u]>0)dfs2(son[u],tp);
	for(int v:g[u]){
		if(v!=fa[u]&&v!=son[u])dfs2(v,v);
	} 
}
int querysumch(int u,int v,int clr){
	int ret=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ret+=blk.querysum(dfn[top[u]],dfn[u],clr),u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	return ret+blk.querysum(dfn[v],dfn[u],clr);
}
int querymxch(int u,int v,int clr){
	int ret=0;
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		ret=max(ret,blk.querymx(dfn[top[u]],dfn[u],clr)),u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	return max(ret,blk.querymx(dfn[v],dfn[u],clr));
}
int main(){
	cin>>n>>q;
	bsz=sqrt(n);
	for(int i=1;i<=n;i++)cin>>w[i]>>c[i];
	for(int i=1,u,v;i<n;i++)cin>>u>>v,g[u].push_back(v),g[v].push_back(u);
	dfs1(1,0);
	dfs2(1,1);
	blk.build();
	while(q--){
		string op;
		int x,y,c,w;
		cin>>op;
		if(op=="CC")cin>>x>>c,blk.updclr(dfn[x],c);
		else if(op=="CW")cin>>x>>w,blk.updval(dfn[x],w);
		else if(op=="QS")cin>>x>>y,cout<<querysumch(x,y,dfn2c[dfn[x]])<<"\n";
		else cin>>x>>y,cout<<querymxch(x,y,dfn2c[dfn[x]])<<"\n"; 
	} 
	return 0;
}
posted @ 2026-07-24 22:17  zheyutao  阅读(4)  评论(0)    收藏  举报