T1 开挂
比较水的贪心题,可以证明一定只包含不相交,拿个栈就很好做了。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=1e6+10;
int n,a[maxn],b[maxn];
vector<int>ned,spt;
void solve(){
cin>>n;Rep(i,1,n)cin>>a[i];Rep(i,1,n)cin>>b[i];
sort(a+1,a+n+1);
Rep(i,2,n){
if(a[i]==a[i-1]){
ned.push_back(a[i]);
}else {
for(int j=a[i-1]+1;j<a[i] && (!ned.empty());++j){
spt.push_back(j-ned.back());ned.pop_back();
}
}
}
int now=a[n]+1;
while(!ned.empty()){ spt.push_back(now-ned.back());ned.pop_back();++now; }
sort(b+1,b+n+1);
ull ans=0;
if(!spt.empty()){
sort(spt.begin(),spt.end());
int it=1;
Dwn(i,spt.size()-1,0){
ans+=1LL*b[it]*spt[i];
++it;
}
}
cout<<ans<<"\n";
}
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
T2 叁仟柒佰万
比较水的dp题,可以处理出最后一个段以i结尾时,前i个数分为若干段的方案数,显然是求出一个最大的左端点使得区间mex恰好达到要求,左端点之前的所有位置都可以转移过来,维护前缀和就行。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=3.7e7+10,Mod=1e9+7;
const ll B=262143;
#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
const int SZ=1<<21;char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){ gc;while(*ip<'-')gc; bool f=*ip=='-';if(f)gc; int x=*ip&15;gc; while(*ip>'-'){x*=10;x+=*ip&15;gc;} return f ? -x : x; }
int tex,n;
int a[maxn];
int Mex,vis[300000],AMex;
int f[maxn],L[maxn];
void solve(){
n=read();
Mex=AMex=0;
if(n==37000000){
int x,y;x=read(),y=read();
a[1]=0;Rep(i,2,n)a[i]=(1LL*a[i-1]*x+y+i)&B;
}else Rep(i,1,n)a[i]=read();
Rep(i,1,n){ ++vis[a[i]]; while(vis[Mex])++Mex; }
Rep(i,1,n)vis[a[i]]=f[i]=0;
AMex=Mex,Mex=0;
int ED=0;
Dwn(i,n,1){
++vis[a[i]];
while(vis[Mex])++Mex;
if(Mex==AMex){ ED=i;break; }
}
Rep(i,ED,n)vis[a[i]]=0;Mex=0;
int l=1,ans=0;
f[0]=1;
for(int i=1;i<ED;++i){
++vis[a[i]];
while(vis[Mex])++Mex;
if(Mex==AMex){
while(l<i && (a[l]>Mex || (vis[a[l]]>1))){--vis[a[l]];++l;}
L[i]=l;
}else L[i]=-1;
if(L[i]!=-1){ f[i]=f[L[i]-1];ans=(ans+f[i])%Mod; }
f[i]=(f[i]+f[i-1])%Mod;
}
Rep(i,1,n)vis[a[i]]=0;
cout<<(ans+1)%Mod<<"\n";
}
int main (){ tex=read();while(tex--)solve();return 0; }
T3 超级加倍
用了笛卡尔树,建出笛卡尔树使得两点树上路径的最大/小值为笛卡尔树上两点的lca,于是在大根/小根笛卡尔树上同时互为祖先关系的点对就是要求的路径数量,求这样的点对就是将一棵树转为dfs序,然后搜另一棵树,用BIT维护当前点到根的路径上出现了哪些dfs序,然后在BIT上查原来有几个点在它子树内就行。
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i)
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;
const int maxn=2e6+10;
int n;
ll ans;
int a[maxn];
struct BIT{
int c[maxn];
#define lowbit(x) (x&-x)
void Add(int x,int d){ for(;x<=n;x+=lowbit(x))c[x]+=d; }
int Query(int x){
int res=0;for(;x;x-=lowbit(x))res+=c[x];return res;
}
int Range(int x,int y){ return Query(y)-Query(x-1); }
}B;
struct Graph{
struct eg{int from,to,next;}e[maxn*2];
int len,head[maxn];int f[maxn];int fa[maxn];bool vis[maxn];
void lqx(int from,int to)
{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
}G;
struct CTT{
struct eg{int from,to,next;}e[maxn*2];
int f[maxn];
int Find(int x){return f[x]==x ? x : f[x]=Find(f[x]);}
void Init(){ Rep(i,1,n)f[i]=i; }
int len,head[maxn];
void lqx(int from,int to)
{ e[++len].from=from,e[len].to=to,e[len].next=head[from],head[from]=len; }
void Build1(){
Init();
Rep(u,1,n){
for(int i=G.head[u];i;i=G.e[i].next){
int v=G.e[i].to;
if(v<u){
if(Find(u)==Find(v))continue;
lqx(Find(u),Find(v));
f[Find(v)]=Find(u);
}
}
}
}
void Build2(){
Init();
Dwn(u,n,1){
for(int i=G.head[u];i;i=G.e[i].next){
int v=G.e[i].to;
if(v>u){
if(Find(u)==Find(v))continue;
lqx(Find(u),Find(v));
f[Find(v)]=Find(u);
}
}
}
}
}T,E;
int st[maxn],ed[maxn],Te;
void Dfs1(int u){
st[u]=++Te;
for(int i=T.head[u];i;i=T.e[i].next){ int v=T.e[i].to; Dfs1(v); } ed[u]=Te;
}
void Dfs2(int u){
ans+=B.Range(st[u],ed[u]);
B.Add(st[u],1);
for(int i=E.head[u];i;i=E.e[i].next){ int v=E.e[i].to; Dfs2(v); }
B.Add(st[u],-1);
}
void solve(){
cin>>n;int x;
Rep(i,1,n){ cin>>x;if(x)G.lqx(i,x),G.lqx(x,i); }
T.Build1(),E.Build2();
Dfs1(n),Dfs2(1);
cout<<ans<<"\n";
}
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }
T4 欢乐豆
提取子图最短路。
如果没有特殊边,那么显然每个源到的所有点最短路都是源的点权。所以当一个点没有被特殊边涉及时,直接如此求即可。对于被特殊边涉及的点,点数规模较小,他们通过特殊边相连构成若干联通块,对于每个块可以求出块内全源最短路,然后算总和。
路径类型分为
- 块内 \(\rightarrow\) 块内
- 块内 \(\rightarrow\) 块外
- 块内 \(\rightarrow\) 块外 \(\rightarrow\) 块内
前两种就普通dij,用线段树模拟,第三种显然只能是在块内走一段,然后走到点权最小的一个块外点再走回来,算答案的时候特殊考虑一下就行
点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=(int)b;++i)
#define Dwn(i,a,b) for(int i=a;i>=(int)b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;
const int maxn=1e5+10,INF=1e17+20051107;
int n,m;
ll ans;
int a[maxn],id[maxn],ind;
multiset<int>MS;
vector<int>pts[maxn];
vector<pii>vec[maxn];
int ud[maxn],dis[maxn];
struct UN{
int f[maxn],siz[maxn];
void Init(int n){ Rep(i,1,n)f[i]=i,siz[i]=1; }
int Find(int x){ return f[x]==x ? x : f[x]=Find(f[x]); }
void Merge(int x,int y){ x=Find(x),y=Find(y); if(x==y)return; f[y]=x,siz[x]+=siz[y]; }
}S;
struct Seg{
struct Tree{ int mini,pos,lazy,tag; }tr[maxn<<2];
void Pushup(int rt){
tr[rt].mini=min(tr[rt<<1].mini,tr[rt<<1|1].mini);
if(tr[rt].mini==tr[rt<<1].mini &&(!tr[rt<<1].tag))tr[rt].pos=tr[rt<<1].pos;
else tr[rt].pos=tr[rt<<1|1].pos;
tr[rt].tag=tr[rt<<1].tag&tr[rt<<1|1].tag;
}
void Build(int rt,int l,int r){
tr[rt].mini=tr[rt].lazy=INF;tr[rt].tag=0,tr[rt].pos=l;
if(l==r)return;
int mid=(l+r)>>1;
Build(rt<<1,l,mid),Build(rt<<1|1,mid+1,r);
}
void Update(int rt,int w){tr[rt].mini=min(tr[rt].mini,w),tr[rt].lazy=min(tr[rt].lazy,w);}
void Pushdown(int rt){
if(tr[rt].lazy==INF)return;
if(!tr[rt<<1].tag)Update(rt<<1,tr[rt].lazy);
if(!tr[rt<<1|1].tag)Update(rt<<1|1,tr[rt].lazy);
tr[rt].lazy=INF;
}
void Delete(int rt,int l,int r,int x){
if(l==r)return tr[rt].mini=INF,tr[rt].tag=1,void();
int mid=(l+r)>>1;Pushdown(rt);
if(x<=mid)Delete(rt<<1,l,mid,x);
else Delete(rt<<1|1,mid+1,r,x);
Pushup(rt);
}
void Modify(int rt,int l,int r,int s,int t,int w){
if(tr[rt].tag || t<s)return;
if(s<=l && t>=r)return Update(rt,w);
int mid=(l+r)>>1;Pushdown(rt);
if(s<=mid)Modify(rt<<1,l,mid,s,t,w);
if(t>mid)Modify(rt<<1|1,mid+1,r,s,t,w);
Pushup(rt);
}
}T;
void Dij(int i,int u){
T.Build(1,1,pts[i].size());
T.Modify(1,1,pts[i].size(),ud[u],ud[u],0);
dis[ud[u]]=0;
while(T.tr[1].mini!=INF){
int x=T.tr[1].pos,dd=T.tr[1].mini,last=1;
dis[x]=dd;T.Delete(1,1,pts[i].size(),x);
x=pts[i][x-1];
for(auto it : vec[x]){
T.Modify(1,1,pts[i].size(),last,ud[it.fir]-1,a[x]+dd);
T.Modify(1,1,pts[i].size(),ud[it.fir],ud[it.fir],dd+it.sec);
last=ud[it.fir]+1;
}
T.Modify(1,1,pts[i].size(),last,pts[i].size(),dd+a[x]);
}
}
void solve(){
cin>>n>>m;Rep(i,1,n)cin>>a[i],MS.insert(a[i]);
int x,y,w;S.Init(n);
Rep(i,1,m){ cin>>x>>y>>w;vec[x].emplace_back(y,w);S.Merge(x,y); }
Rep(i,1,n){
sort(vec[i].begin(),vec[i].end());
if(S.Find(i)==i && S.siz[i]>1)id[i]=++ind;
}
Rep(i,1,n){
if(S.siz[S.Find(i)]>1)pts[id[S.Find(i)]].push_back(i);
else ans+=(n-1)*a[i];
}
Rep(i,1,ind){
for(int j=0;j<(int)pts[i].size();++j){ ud[pts[i][j]]=j+1;MS.erase(MS.find(a[pts[i][j]])); }
int tmp= MS.empty() ? INF : (*MS.begin());
for(auto u : pts[i]){
Dij(i,u);
int mini=INF;
Rep(j,1,pts[i].size())mini=min(mini,dis[j]+a[pts[i][j-1]]);
Rep(j,1,pts[i].size())ans+=min(dis[j],mini+tmp);
ans+=mini*(n-pts[i].size());
}
for(auto u : pts[i])MS.insert(a[u]);
}
cout<<ans<<"\n";
}
#undef int
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }