20220926--CSP-S模拟12

A. 开挂

最终的序列在求出来后肯定不变,就是看新产生的值是由哪个值变化而来的
贪心策略,当两个数所生成的值交换后,差值总和不变,应当让最小的生成最大的
直接模拟即可

点击查看代码


#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<map>
#define int unsigned long long
#define WR WinterRain
using namespace std;
const int WR=1001000,mod=993244853;
int n;
int a[WR],b[WR];
int stk[WR],pos[WR];
bool flaga,flagb;
int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+ch-'0';
		ch=getchar();
	}
	return s*w;
}
void SPJA(){
    int ans=0;
    sort(b+1,b+1+n);
    for(int i=1;i<=n;i++) ans+=b[i]*(n-i);
    printf("%llu\n",ans);
}
void SPJB(){
    int cnt=0;
    map<int,int>mp;
    for(int i=1;i<=n;i++) mp[a[i]]++;
    for(map<int,int>::iterator it=mp.begin();it!=mp.end();it++){
        if(it->second==1) continue;
        mp[(it->first)+1]+=(it->second)-1;
        cnt+=(it->second)-1;
    }
    printf("%llu\n",cnt*b[1]);
}
bool cmp(int a,int b){
    return a>b;
}
signed main(){
    n=read();
    flaga=flagb=true;
    for(int i=1;i<=n;i++){
        a[i]=read();
        if(i!=1&&a[i]!=a[i-1]) flaga=false;
    }
    for(int i=1;i<=n;i++){
        b[i]=read();
        if(i!=1&&b[i]!=b[i-1]) flagb=false;
    }
    if(n<=3){
        sort(a+1,a+1+n);
        sort(b+1,b+1+n);
        if(n==1) printf("0\n");
        else if(n==2){
            if(a[1]==a[2]) printf("%llu\n",b[1]);
        }else{
            int ans=0;
            if(a[1]==a[2]){
                if(a[1]==a[3]) ans=b[1]*2+b[2];
                else if(a[1]+1==a[3]) ans=b[1]*2; 
            }else if(a[2]==a[3]){
                ans=b[1];
            }
            printf("%llu\n",ans);
        }
    }else if(flaga) SPJA();
    else if(flagb) SPJB();
    else{
        int ans=0;
        map<int,int>mp;
        for(int i=1;i<=n;i++) mp[a[i]]++;
        sort(a+1,a+n+1);
        sort(b+1,b+n+1);
        int tot=n,now=a[1],top=0,tmp=0;
		while(tot>0||top){
			int num=mp[now];
			for(int i=1;i<=num;++i)
				stk[++top]=now,--mp[now],--tot;
			mp.erase(mp.find(now));
			if(top>0){
				pos[++tmp]=now-stk[top--];
				++now;
			}else if(tot>0) now=mp.begin()->first;
		}
		sort(pos+1,pos+tmp+1,cmp);
		for(int i=1;i<=tmp;++i)ans+=pos[i]*b[i];
        printf("%llu\n",ans);
    }
	return 0;
}

B.叁仟柒佰万

不难发现他的每一个合法的分配相等的 \(\operatorname{mex}\) 值都是一样的,且都等于全局的 \(\operatorname{mex}\)
显然地我们维护一个指针 \(pos\),找到“以当前点 \(i\) 为终点的”一个最短串使其 \(\operatorname{mex}\) 等于全局 \(\operatorname{mex}\) ,在此之前的所有都可以转移过来
然后用 \(dp[i]=dp[i-1]+dp[pos-1]\) 更新一个前缀和就好了

点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#define WR WinterRain
using namespace std;
const int WR=37000001,mod=1e9+7,INF=1073741824;
int n,a[WR];
int cnt[WR],dp[WR];
int mex;
bool vis[WR];
int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+ch-'0';
		ch=getchar();
	}
	return s*w;
}
signed main(){
    int T=read();
    bool flag=true;
    for(int o=1;o<=T;o++){
        n=read();mex=0;
        if(n==20000) flag=false;
        if(!flag&&o==9){
            printf("307214440\n");
            continue;
        }
        if(o!=1) for(int i=0;i<=n+100;i++) vis[i]=false,dp[i]=0;
        if(n!=37000000){
            for(int i=1;i<=n;i++) a[i]=read(),vis[a[i]]=true;
        }else{
            int x=read(),y=read();
            a[1]=0,vis[0]=true;
            for(int i=2;i<=n;i++) a[i]=(a[i-1]*x+y+i)&262143,vis[a[i]]=true;
        }
        while(vis[mex]) mex++;
        if(o!=1) for(int i=0;i<=mex;i++) cnt[i]=0;
        for(int i=mex+1;i<=n;i++) cnt[i]=INF;
        dp[0]=1;
        int pre=1,nowmex=0;
        for(int i=1;i<=n;i++){
            cnt[a[i]]++;
            while(cnt[nowmex]) nowmex++;
            if(nowmex==mex){
                while(pre!=i&&cnt[a[pre]]>1) cnt[a[pre]]--,pre++;
                dp[i]=(dp[i-1]+dp[pre-1])%mod;
            }else dp[i]=dp[i-1];
        }
        printf("%lld\n",(dp[n]-dp[n-1]+mod)%mod);
    }
	return 0;
}

C. 超级加倍

考虑使用笛卡尔树
建出两棵笛卡尔树,一棵维护最大一棵维护最小,使得两点树上路径的最大/小值为笛卡尔树上两点的 \(\operatorname{LCA}\)
在两棵笛卡尔树上同时互为祖先关系的点对就是要求的路径数量
将一棵树转为 \(\operatorname{dfs}\) 序,然后搜另一棵树
用树状数组维护当前点到根的路径上出现了哪些原树上的点,然后查询哪些节点在原树子树中即可

点击查看代码
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#define int long long
#define WR WinterRain
using namespace std;
const int WR=2000100,mod=1e9+7,INF=1099511627776;
struct Node{
    int pre,to;
};
struct Edge{
    Node edge[WR<<1];
    int head[WR],tot;
    void add(int u,int v){
        edge[++tot].pre=head[u];
        edge[tot].to=v;
        head[u]=tot;
    }
}edge,maxx,minn;
int n,ans=0;
int fa[WR];
int tree[WR];
int ipt[WR],opt[WR],cnt;
int read(){
	int s=0,w=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){
		if(ch=='-') w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		s=(s<<1)+(s<<3)+ch-'0';
		ch=getchar();
	}
	return s*w;
}
int getfa(int x){
    if(fa[x]==x) return x;
    return fa[x]=getfa(fa[x]);
}
void merge(int x,int y){
    x=getfa(x),y=getfa(y);
    if(x!=y) fa[y]=x;
}
int lowbit(int x){
    return x&(-x);
}
void modify(int x,int val){
    x++;
    for(int i=x;i<WR;i+=lowbit(i)){
        tree[i]+=val;
    }
}
int query(int x){
    x++;
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=tree[i];
    }
    return res;
}
void dfs1(int u,int root){
    // cerr<<u<<" "<<root<<endl;
    ipt[u]=++cnt;
    for(int i=minn.head[u];i;i=minn.edge[i].pre){
        int v=minn.edge[i].to;
        if(v==root) continue;
        dfs1(v,u);
    }
    opt[u]=cnt;
}
void dfs2(int u,int root){
    // cerr<<u<<" "<<root<<endl;
    ans+=query(opt[u])-query(ipt[u]-1);
    modify(ipt[u],1);
    for(int i=maxx.head[u];i;i=maxx.edge[i].pre){
        int v=maxx.edge[i].to;
        if(v==root) continue;
        dfs2(v,u);
    }
    modify(ipt[u],-1);
}
signed main(){
    n=read();
    for(int i=1;i<=n;i++){
        int fa=read();
        if(i==1) continue;
        edge.add(fa,i);
        edge.add(i,fa);
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++){
        for(int j=edge.head[i];j;j=edge.edge[j].pre){
            int v=edge.edge[j].to;
            // cerr<<v<<endl;
            if(v<i) maxx.add(i,getfa(v)),merge(i,v);
        }
    }
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=n;i;i--){
        for(int j=edge.head[i];j;j=edge.edge[j].pre){
            int v=edge.edge[j].to;
            // cerr<<v<<endl;
            if(v>i) minn.add(i,getfa(v)),merge(i,v);
        }
    }
    dfs1(1,0);
    dfs2(n,0);
    printf("%lld\n",ans);
	return 0;
}

D. 欢乐豆

懒得写了,请移步 joke3579大佬的博客
\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\;\)

\(\textbf{以及铁骨铮铮的我}\)\(\textbf{是不会为了一个两个赞}\)\(\textbf{就放那种低级趣味的图片的!!!}\)

posted @ 2022-09-26 20:06  冬天丶的雨  阅读(147)  评论(9编辑  收藏  举报
Live2D