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{就放那种低级趣味的图片的!!!}\)
本文来自博客园,作者:冬天丶的雨,转载请注明原文链接:https://www.cnblogs.com/WintersRain/p/16732147.html
为了一切不改变的理想,为了改变不理想的一切