1. 程式人生 > 其它 >2022.4.10普及組模擬

2022.4.10普及組模擬

A 排座位

沒有任何難度的大水題

想複雜了。如果只能是兩個相鄰的位置交換自然要求逆序對,但是這個題可以隨便換,所以我們亂搞。最終的目標就是把 \(1\) 換到第一個位置, \(2\) 換到第二個位置,以此類推

如果 \(data_i=i\) 就不操作,如果 $data_i != i $ 且 \(data_{pos} = i\) 則交換。很顯然的做法,但是乍一看貌似無法保證交換次數最小,所以有另一個很嚴謹的做法

\(Code\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000+5;
inline int read(){
	int x=0;char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9'){
		x=(x<<1)+(x<<3)+(c^'0');c=getchar();
	}
	return x;
}
int data[maxn],pos[maxn];
int n;
int main(){
	freopen("seat.in","r",stdin);
	freopen("seat.out","w",stdout);
	n=read();
	for(int i=1;i<=n;++i){
		data[i]=read();
		pos[data[i]]=i;
	}
	int ans=0;
	for(int i=1;i<=n;++i){
	    if(data[i]!=i){
		    pos[data[i]]=pos[i];
	    	swap(data[i],data[pos[i]]);
		    ans++;
		}
	}
	printf("%d\n",ans);
	return 0;
}

B 夢中的學校(原題金字塔,《演算法競賽進階指南》區間DP部分)

對於一個字串 \(S_l - S_r\)可能會有多種情況,所以考慮區間DP。定義狀態 \(f_{i,j}\)\(S_l - S_r\) 的情況數。

我們只考慮其構成一顆樹的情況,所以 \(S_l!=S_r\) 的情況是不合法,dfs肯定要從根進去再從根出來。

如果 \(l=r\) 很顯然方案數為1

為了不重複計數,只考慮該範圍中第一顆子樹的範圍,列舉斷點 \(k\)\(l-r\) 被分為 \(l+1--k-1\)\(k--r-1\)兩部分,\(k\) 不相同,第一顆子樹大小也就不相同,就不可能產生重複的結構,則有方程

\(f_{i,j}\)=\(f_{i,j} + f_{i+1,k-1} * f_{k,j-1}\)

狀態之間遵循加法原理而子狀態之間遵循乘法原理

Code

int solve(int l,int r){
    if(l>r)return 0;
    if(S[l]!=S[r])return f[l][r]=0;
    if(l==r)return f[l][r]=1;
    if(f[l][r]!=-1)return f[l][r];
    f[l][r]=0;
    for(int k=l+2;k<=r;++k){
        f[l][r]=(f[l][r]+1ll*solve(l+1,k-1)*solve(k,r))%mod;
    }
    return f[l][r];
}
printf("%d\n",solve(1,strlen(S+1)));

C 激流突進(SCOI2011糖果)

顯然是一個差分約束系統

如果\(u>v\),則\(<u,v>=1\)

如果\(u<v\),則\(<v,u>=1\)

如果\(u=v\),則\(<u,v>=<u,v>=0\)

如果\(u<=v\),則\(<v,u>=0\)

如果\(u>=v\),則\(<u,v>=0\)

如果A>B>C,則<B,A>=<C,B>=<C,A>=1,為了使結果正確,建完圖之後跑最長路。

建完圖之後很有可能不完全,設一個解\(X_0=0\),由於其他的解都是正解,很顯然有\(\forall i\in [1,n]\),都有\(X_i>=X_0\),即\(X_i-X_0>=0\),0向所有的點連一條權值為0的邊即可

不成立的情況:

如果A>B,B>C,C>A,顯然不成立,而且建圖後出現環,可以用topo排序處理。最長路演算法常用spfa,且spfa跑最長路可以判正環,所以用spfa處理

如果A>A 或 A<A ,顯然不成立

這樣處理完之後貌似就沒什麼問題,但是除了正環以外還有另外一種環,A=B=C,邊權都為0,所以spfa掃它沒啥意義,所以連完1,3,5三種情況後Tarjan縮點,再連2,4情況的邊

struct Graph{
    struct edge{
        int to,next,dis;
    }e[maxn*5];
    int head[maxn],len;
    void Insert(int x,int y,int dis){
         e[++len].to=y;e[len].dis=dis;e[len].next=head[x],head[x]=len;
    }
    bool vis[maxn];
    long long cnt[maxn],d[maxn];
    bool spfa(int u){
        memset(d,0xf3,sizeof(d));
        queue<int>q;
        q.push(u);
        d[u]=1;
        vis[u]=1;
        cnt[u]=1;
        while(!q.empty()){
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=e[i].next){
                int v=e[i].to;
                if(d[v]<d[x]+e[i].dis){
                    d[v]=d[x]+e[i].dis;
                    if(!vis[v]){
                        q.push(v);
                        cnt[v]++;
                        vis[v]=1;
                        if(cnt[v]>n){
                            return 1;
                        }
                    }
                }
            }
        }
        return 0;
    }
}G;
void work(){
     n=read(),m=read();
     bool f=0;
     for(int i=1;i<=m;++i){
         int op=read(),x=read(),y=read();
         if(op==1)G.Insert(x,y,0),G.Insert(y,x,0);
         if(op==2){if(x==y)f=1;G.Insert(x,y,1);}
         if(op==3)G.Insert(y,x,0);
         if(op==4){if(x==y)f=1;G.Insert(y,x,1);}
         if(op==5)G.Insert(x,y,0);      
     }
     if(f){
         printf("-1\n");return;
     }    
     for(int i=n;i>0;--i)G.Insert(0,i,0);//玄學優化
     f=G.spfa(0);
     if(f){
         printf("-1\n");return;
     }
     long long ans=0;
     for(int i=1;i<=n;++i){
         ans+=G.d[i];
     }
     printf("%lld\n",ans);
}
int main(){
    work();
    return 0;
}

D 獎學金

先按照成績排序,列舉每一個數作為中位數,答案為\(money_i\)+前n/2最小值之和+後n/2最小值之和。最小值之和用優先佇列維護即可

賽時十分鐘打了個線段樹,每次列舉重新建樹,暴力求最值 \(60pts\)

Code

int data[maxn],pos[maxn];
struct Pig{
	long long cs,mo;
}pig[maxn];
bool cmp(Pig x,Pig y){
	return x.cs<y.cs;
}
int n,c,F;
int f[maxn],g[maxn];//f[i] i為中位數前n/2最小值的和, g[i] i為中位數後n/2最小值的和 
void work(){
    priority_queue<long long>q;
	long long sum=0;
    for(int i=1;i<=n/2;++i){
		q.push(pig[i].mo);sum+=pig[i].mo;
	}
	for(int i=n/2+1;i<=c-n/2;++i){
		f[i]=sum;
		if(pig[i].mo<q.top()){sum-=q.top();q.pop();q.push(pig[i].mo);sum+=pig[i].mo;}
	}
	q=priority_queue<long long>();sum=0;
	for(int i=c;i>=c-n/2+1;--i){
		q.push(pig[i].mo);sum+=pig[i].mo;
	}
	for(int i=c-n/2;i>=n/2+1;--i){
		g[i]=sum;
		if(pig[i].mo<q.top()){sum-=q.top();q.pop();q.push(pig[i].mo);sum+=pig[i].mo;}
	}
}
int main(){
	freopen("money.in","r",stdin);
	freopen("money.out","w",stdout);
	n=read(),c=read(),F=read();
	for(int i=1;i<=c;++i){
	    pig[i].cs=read(),pig[i].mo=read();
	}
	sort(pig+1,pig+c+1,cmp);
	long long ans=-1;
	work();
	for(int i=c-n/2;i>=n/2+1;--i){
		if(f[i]+g[i]+pig[i].mo<=F){
			ans=pig[i].cs;break;
		}
	}
	printf("%lld\n",ans);
	return 0;
}