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;
}

浙公网安备 33010602011771号