CSP模拟12
卢本伟nb。
T1
其实挺水。感觉懒得写就不写了。
赛时和joke3579打的multiset然后双双被卡。赛后看了看crs_line的做法。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <stack>
using namespace std;
int n,num,a[2000010],b[2000010],cnt[2000010];
bool cmp(int a,int b){
return a>b;
}
stack<int>s;
unsigned long long ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
sort(b+1,b+n+1);
sort(a+1,a+n+1);
a[n+1]=2147483647;
int ret=0;
for(int i=1;i<=n+1;i++){
if(a[i]!=a[i-1]){
while(!s.empty()&&ret<a[i]){
cnt[++num]=ret-s.top();s.pop();
ret++;
}
ret=a[i]+1;
}
else s.push(a[i]);
}
sort(cnt+1,cnt+n+1,cmp);
for(int i=1;i<=num;i++)ans+=1ll*cnt[i]*b[i];
printf("%llu",ans);
}
T2
挺好玩的题。
首先我们有一个结论:每个子段的 \(mex\) 就是整个序列的 \(mex\) 。
考虑:如果整个序列有 \(0\) ,那么所有区间都得放一个 \(0\) 。如果整个序列有 \(1\) ,那么所有区间都得放一个 \(1\) 。以此类推,只要有就得放,放到没有就是 \(mex\) 。
然后可以 \(dp\) :设 \(dp[i]\) 为扫到 \(i\) 的答案:那么我们找到每个 \(i\) 往前的最后面的使得 \(mex\) 为整个区间的 \(mex\) 的位置 \(pos[i]\) ,然后就有一个比较搞的能那前缀和乱搞过去的柿子。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int mod=1000000007;
int n,mex,pos[37000001],a[37000001],v[300010],sum[37000001];
void csh(){
memset(pos,-1,sizeof(pos));
memset(v,0,sizeof(v));
memset(sum,0,sizeof(sum));
}
int main(){
int tim;scanf("%d",&tim);
int ret=tim;
while(tim--){
if(ret!=tim+1)csh();
scanf("%d",&n);
if(n==37000000){
int x,y;scanf("%d%d",&x,&y);
a[1]=0;
for(int i=2;i<=n;i++){
a[i]=(a[i-1]*x+y+i)&262143;
v[a[i]]++;
}
}
else{
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]>n+2)a[i]=n+2;
v[a[i]]++;
}
}
for(int i=0;i<=n;i++){
if(!v[i]){
mex=i;break;
}
}
memset(v,0,sizeof(v));
int cnt=0;
for(int i=n;i>=1;i--){
if(a[i]<mex){
v[a[i]]++;
if(v[a[i]]==1)cnt++;
}
if(cnt==mex){
pos[n]=i;break;
}
}
if(pos[n]==-1)pos[n]=1;
for(int i=n-1;i>=1;i--){
if(a[i+1]<mex){
v[a[i+1]]--;
if(v[a[i+1]]==0)cnt--;
}
int p=pos[i+1];
while(p>0&&cnt<mex){
p--;
if(a[p]<mex){
v[a[p]]++;
if(v[a[p]]==1)cnt++;
}
}
pos[i]=min(p,i);
}
sum[0]=1;
for(int i=1;i<=n;i++){
if(pos[i]==-1)sum[i]=sum[i-1];
else sum[i]=(sum[i-1]+sum[pos[i]-1])%mod;
}
printf("%d\n",(sum[n]-sum[n-1]+mod)%mod);
}
}
T3
好题。
考虑在树上建个笛卡尔树。建树的过程是:(以大根堆序为例)
- 从小到大扫描每个节点。
- 扫描到一个节点时,判断所有与该节点相连的连通块。
- 如果连通块的代表元小于该节点,则将该节点并入连通块,代表元设为该节点,并以该节点为父亲与代表元连边。
然后我们建出大根堆小根堆两棵树,问题就变成了统计一个点在一棵树内的子树内满足在另一棵树上是它的祖先的节点数量,dfs序大力乱搞。
/*当年陈刀仔用二十块赢到三千七百万,今天我卢本伟用二十万赢到两千万,不是问题。
反手一个超级加倍,闷声发大财。
给阿姨倒一杯卡布奇诺。
今天你十七张牌你能秒杀我,*/
#include <algorithm>
#include <cstdio>
#include <iostream>
#define int long long
using namespace std;
struct node{
int v,next;
}edge[8000010];
int n,ans,num,t,head[2000010][3],fa[2000010],st[2000010],ed[2000010];
int c[2000010];
void add(int u,int v,int id){
edge[++t].v=v;edge[t].next=head[u][id];head[u][id]=t;
}
int find(int x){
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
int lowbit(int x){
return x&-x;
}
void update(int x,int val){
while(x<=n){
c[x]+=val;x+=lowbit(x);
}
}
int query(int x){
int sum=0;
while(x){
sum+=c[x];x-=lowbit(x);
}
return sum;
}
void dfs1(int x){
st[x]=++num;
for(int i=head[x][2];i;i=edge[i].next)dfs1(edge[i].v);
ed[x]=num;
}
void dfs2(int x){
ans+=query(ed[x])-query(st[x]-1);
update(st[x],1);
for(int i=head[x][1];i;i=edge[i].next)dfs2(edge[i].v);
update(st[x],-1);
}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
fa[i]=i;
int x;scanf("%lld",&x);
if(x!=0){
add(x,i,0);add(i,x,0);
}
}
for(int x=1;x<=n;x++){
for(int i=head[x][0];i;i=edge[i].next){
int ret=find(edge[i].v);
if(edge[i].v<x&&ret!=x){
add(x,ret,1);fa[ret]=x;
}
}
}
for(int i=1;i<=n;i++)fa[i]=i;
for(int x=n;x>=1;x--){
for(int i=head[x][0];i;i=edge[i].next){
int ret=find(edge[i].v);
if(edge[i].v>x&&ret!=x){
add(x,ret,2);fa[ret]=x;
}
}
}
dfs1(1);dfs2(n);
printf("%lld",ans);
}
T4
不会。贺的。
/*进行一个欢乐的送*/
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <vector>
#include <queue>
#define int long long
using namespace std;
int n,m,ret,minn,s[100010],top,num[100010];
int a[100010],ans;
bool vis[100010];
struct stu{
int v,w,next;
}edge[400010];
int t,head[100010];
void add(int u,int v,int w){
edge[++t].v=v;edge[t].w=w;edge[t].next=head[u];head[u]=t;
}
void make(int x){
if(num[x]==0){
s[++top]=x;num[x]=top;
}
}
struct node{
int x,w;
bool v;
bool operator<(const node &s)const{
return w>s.w;
}
};
priority_queue<node>q;
int fa[100010];
int find(int x){
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
int dis[100010];
void insert(int x,int val) {
if(fa[x]==x){
fa[x]=x+1;dis[x]=val;
q.push({x,dis[x]+a[s[x]],true});
for(int i=head[x];i;i=edge[i].next){
if(dis[edge[i].v]>dis[x]+edge[i].w){
dis[edge[i].v]=dis[x]+edge[i].w;
q.push({edge[i].v,dis[edge[i].v],false});
}
}
}
}
void dijkstra(int st){
for(int i=1;i<=top+1;i++)dis[i]=114514191981000,fa[i]=i;
dis[st]=0;q.push({st,0,false});
while(!q.empty()){
node x=q.top();q.pop();
if(!x.v){
insert(x.x,x.w);
continue;
}
for(int i=head[x.x];i;i=edge[i].next)vis[edge[i].v]=true;
for(int i=find(1);i<=top;i=find(i+1)) {
if(!vis[i])insert(i,x.w);
}
for(int i=head[x.x];i;i=edge[i].next)vis[edge[i].v]=false;
}
}
signed main() {
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=m;i++){
int u,v,w;scanf("%lld%lld%lld",&u,&v,&w);
make(u);make(v);
add(num[u],num[v],w);
}
int ret,minn=2147483647;
for(int i=1;i<=n;i++){
if(!num[i]&&a[i]<minn){
ret=i;minn=a[i];
}
}
if(minn!=2147483647)make(ret);
for(int i=1;i<=n;i++){
if(!num[i])ans+=(n-1)*a[i];
}
for(int i=1;i<=top;i++){
int minn=114514191981000;
dijkstra(i);
for(int j=1;j<=top;j++)ans+=dis[j],minn=min(minn,dis[j]+a[s[j]]);
ans+=(n-top)*minn;
}
printf("%lld",ans);
return 0;
}
快踩