SDU暑期集训排位(2)
作者:互联网
A - Art
签到题。暴力枚举累计答案即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 int a[5]; 5 int main() { 6 for(int i=0;i<5;i++) scanf("%d",a+i); 7 std::sort(a,a+5);int ans=0; 8 for(int i=0;i<5;i++) for(int j=i+1;j<5;j++) for(int k=j+1;k<5;k++) if(a[i]+a[j]>a[k]) ans++; 9 printf("%d\n",ans); 10 return 0; 11 }View Code
B - Biology
模拟题。总共有$100$种牌,去掉手中的两张,在剩下的$98$张中枚举所有的可能,然后按照题意确定手牌类型累加答案。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<vector> 5 #include<algorithm> 6 using namespace std; 7 typedef pair<int,int>P; 8 P a[6]; 9 int rk[30], su[10]; 10 int check(){ 11 for(int i=0;i<25;i++) rk[i]=0; 12 for(int i=0;i<4;i++) su[i]=0; 13 bool st=true; 14 for(int i=1;i<=5;i++){ 15 rk[a[i].first]++; su[a[i].second]++; if(i==1) continue; 16 if(a[i].first-a[i-1].first!=1) st=false; 17 } 18 if(st&&su[a[1].second]==5) return 1; 19 for(int i=1;i<=5;i++) if(rk[a[i].first]>=4) return 2; 20 for(int i=1;i<=5;i++){ 21 for(int j=1;j<=5;j++) if(a[i].first!=a[j].first&&rk[a[i].first]==3&&rk[a[j].first]==2) return 3; 22 } 23 if(su[a[1].second]==5) return 4; 24 if(st) return 5; 25 for(int i=1;i<=5;i++) if(rk[a[i].first]>=3) return 6; 26 for(int i=1;i<=5;i++) for(int j=1;j<=5;j++){ 27 if(a[i].first!=a[j].first&&rk[a[i].first]>=2&&rk[a[j].first]>=2) return 7; 28 } 29 for(int i=1;i<=5;i++) if(rk[a[i].first]>=2) return 8; 30 return 9; 31 } 32 int n, m; 33 int a1, a2, b1, b2; 34 P get(int x){ 35 return P(x/m,x%m); 36 } 37 int ans[10]; 38 int main(){ 39 scanf("%d%d",&n,&m); 40 scanf("%d%d%d%d",&a1,&a2,&b1,&b2); 41 P f=P(a1,a2); P g=P(b1,b2); int v1=a1*m+a2; int v2=b1*m+b2; 42 for(int i=0;i<n*m;i++){ 43 if(i==v1||i==v2) continue; 44 for(int j=i+1;j<n*m;j++){ 45 if(j==v1||j==v2) continue; 46 for(int k=j+1;k<n*m;k++){ 47 if(k==v1||k==v2) continue; 48 a[1]=get(i); a[2]=get(j); a[3]=get(k); a[4]=f; a[5]=g; 49 sort(a+1,a+5+1); 50 ans[check()]++; 51 } 52 } 53 } 54 for(int i=1;i<=9;i++) printf("%d%c",ans[i],(i==9?'\n':' ')); 55 return 0; 56 }View Code
C - Computer Science
单调队列。首先,很显然的一个性质是,如果我们将$a$排序,那么每个区间包含的肯定是$a$的一个子区间,且从贪心的角度考虑,我们肯定希望每个区间恰好包含$K$个点。因此,我们可以对每个$a_i$求出最短的区间的长度,然后在取$max$即可。为了更方便的求符合要求的包含$a_i$的最短区间的长度,我们可以定一个$b$,其中$b_i=a_i-a_{i-k+1}$,那么显然,包含的$a_i$的最短区间的长度为$min_{j=i}^{i+k-1}b_j$,这个显然可以用线段树或者$ST$表来求,但是还有更加简单的办法,就是单调队列,这里不赘述。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 typedef long long ll; 5 const int N=2e5+5; 6 ll a[N],b[N],f[N]; 7 int n,k; 8 int q[N],l,r; 9 int main() { 10 scanf("%d%d",&n,&k); 11 for(int i=1;i<=n;i++) scanf("%lld",a+i); 12 std::sort(a+1,a+1+n); 13 for(int i=k;i<=n;i++) b[i]=a[i]-a[i-k+1]; 14 l=r=0; 15 for(int i=1;i<=n;i++) { 16 if(i+k-1<=n) while(r>l&&b[i+k-1]<=b[q[r-1]]) --r; 17 while(l<r&&q[l]<i) l++; 18 q[r++]=i+k-1; 19 f[i]=b[q[l]]; 20 } 21 ll ans=0; 22 for(int i=1;i<=n;i++) ans=std::max(ans,f[i]); 23 printf("%lld\n",ans); 24 return 0; 25 }View Code
G - New Keyboard
$dp$。对于这个题来说,比较容易想到的状态是用$dp[i][j]$表示已经输入了$i$个字符,最后一次输字符是在第$j$个$layout$下,这显然是可以的,但状态转移会比较麻烦,我们需要枚举下一个$layout$用哪个,同时,这样的时间复杂度也过高了。仔细想想可以发现,以上状态无法解决的问题无非是移动的代价,我们只能枚举距离,而无法直接移动到下一个$layout$,因此我们可以给状态加一维,用$dp[i][j][k]$表示已经输入了$i$个字符,当前是在第$j$个$layout$,并用$k=0$表示当前$layout$没有输入字符,$k=1$反之。状态转移的话,我们可以枚举$i$,然后显然,我们应该是先移动,再输入字符,对于移动来说,我们有$dp[i][j][0]=min(dp[i][(j-1+n)\%n+1][0]+b,dp[i][(j-1+n)\%n+1][1]+a)$,如果你把它的状态转移图画出来,它会成一个简单环,因此,我们可以暴力迭代,在$O(n)$的时间复杂度内就可以完成移动对应的状态转移,而输入字符就很简单了,这里不多说了,详细的细节请看代码。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 typedef long long ll; 5 const int N=2005; 6 ll dp[N][N][2]; 7 int f[N]; 8 char t[N]; 9 const ll INF=1e18; 10 int main() { 11 int n,a,b,c,m; 12 scanf("%d%d%d%d",&n,&a,&b,&c); 13 for(int i=0;i<n;i++) { 14 scanf("%s",t); 15 f[i]=0; 16 for(int j=0;t[j];j++) f[i]|=1<<(t[j]-'0'); 17 } 18 scanf("%s",t+1); 19 m=strlen(t+1); 20 for(int i=0;i<=m;i++) for(int j=0;j<n;j++) dp[i][j][0]=dp[i][j][1]=INF; 21 dp[0][0][1]=0; 22 for(int i=0;i<m;i++) { 23 while(1) { 24 bool f=false; 25 for(int j=0;j<n;j++) { 26 ll t=std::min(dp[i][j][0]+b,dp[i][j][1]+a); 27 if(t<dp[i][(j+1)%n][0]) { 28 f=true; 29 dp[i][(j+1)%n][0]=t; 30 } 31 } 32 if(!f) break; 33 } 34 int v=t[i+1]-'0'; 35 for(int j=0;j<n;j++) if(f[j]>>v&1) { 36 dp[i+1][j][1]=std::min(dp[i+1][j][1],std::min(dp[i][j][0],dp[i][j][1])+c); 37 } 38 } 39 ll ans=INF; 40 for(int i=0;i<n;i++) ans=std::min(ans,dp[m][i][1]); 41 if(ans>=INF) ans=-1; 42 printf("%lld\n",ans); 43 return 0; 44 }View Code
H - Folding the Figure
$暴力搜索$。首先,很容易发现$k<=n+n$,因为图形折叠以后至少会保留一半的面积。然后,对称轴只有四个,且四个都有解!因此,我们随便找其中一个对称轴,然后暴力的搜索一个大小为$k-n$的连通块,并把坐标按照对称轴对称一下,就是答案。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<vector> 5 #include<set> 6 const int N=1e5+5; 7 typedef std::pair<int,int> P; 8 P a[N]; 9 int n,k,T; 10 std::set<P> s,t; 11 const int fx[4]={0,0,1,-1}; 12 const int fy[4]={1,-1,0,0}; 13 void dfs(int x,int y,int up) { 14 if(t.size()>=up) return; 15 t.insert(P(x,y)); 16 for(int i=0;i<4;i++) { 17 P c(x+fx[i],y+fy[i]); 18 if(s.count(c)&&!t.count(c)) dfs(c.first,c.second,up); 19 } 20 } 21 int main() { 22 scanf("%d",&T); 23 while(T--) { 24 scanf("%d%d",&n,&k); 25 std::vector<P> ans;s.clear();t.clear(); 26 for(int i=1;i<=n;i++) { 27 scanf("%d%d",&a[i].first,&a[i].second); 28 ans.push_back(a[i]); 29 s.insert(a[i]); 30 } 31 std::sort(a+1,a+1+n); 32 printf("L %d\n",a[1].first); 33 dfs(a[1].first,a[1].second,k-n); 34 for(P c:t) ans.push_back(P(a[1].first+a[1].first-c.first-1,c.second)); 35 std::sort(ans.begin(),ans.end()); 36 for(P c:ans) printf("%d %d\n",c.first,c.second); 37 } 38 return 0; 39 }View Code
J - Joining Arrays
贪心、$dp$。一般这类找字典序最小的题,都是从前往后贪心,枚举每一位,判断该位填某个数字之后是否有可行解,本题同理。现在的问题就是如何判断存在可行解?我们可以先把问题反过来思考,给你一个序列,如何判断它是$A$和$B$的一个$join$(不考虑字典序)?那么我们可能很容易想到一个三维$dp$,用$dp[i][j][k]$表示$A$匹配的最后一个位置为$j$,$B$匹配的最后一个位置为$k$,已经匹配了$i$个位置是否可行,然后这个东西其实可以变两维,用$dp[i][j]$表示$A$匹配的最后一个位置是$j$,已经匹配了$i$个位置时,$B$匹配到的最小位置,转移的话,我们可以对$A$和$B$都预处理一个$nxt$数组,维护第$i$个位置后的最近的数字$j$的位置,然后分别尝试用$A$和$B$去匹配下一个数字即可。现在我们考虑动态的维护这个$dp$,初始化$dp[0][0]=0$,假如我们现在已经填了$i-1$个数字了,接下来要填第$i$个,我们可以暴力枚举要填的数字,然后就可以进行$dp$,完成$dp$后,我们只要$check$一下是否存在这样的$j$,使得$n-j+m-dp[i][j] \geq k-i$,如果存在,则该数字可以填,否则不可以。这样的复杂度是$O(nkC)$,其中$C$是数字种类数,这个复杂度显然无法接受,但是仔细想想可以发现,我们没有必要枚举每一位放什么,我们完全可以二分,即,我们可以二分一个$x$,然后$check$一下$[1,x]$中是否含有一个数字存在可行解,原来是$check$一个,现在是$check$区间,这个怎么做呢?其实很简单,我们只要把$nxt$数组对数字累计前缀最小即可,即用$nxt[i][j]$表示第$i$个位置后,最近的一个属于$[1,j]$的数字的位置,其他的跟原来一样,这样时间复杂度就变为了$O(nklogC)$。但是我们并没有做完,题目中还有一个限制条件,就是$A$和$B$都必须对答案有贡献,即,最终答案不能只存在于$A$或$B$中,其实这个很容易搞定,我们先按照之前的做法找到一个序列,然后在$check$一下这个序列是否可以只存在于$A$或者$B$中,如果可以只存在于$A$,那么我们就把序列的最后一个数字换成$B$序列的最小值,另一种情况反之。
1 #include<iostream> 2 #include<cstdio> 3 const int N=3001; 4 typedef std::pair<int,int> P; 5 int n,m,k,nxt[2][N][N],pos[N],a[2][N],ans[N<<1],pre[2][N][N]; 6 int dp[2][N]; 7 void init(int n,int nxt[N][N],int pre[N][N],int a[N]) { 8 for(int i=1;i<=n;i++) scanf("%d",a+i); 9 for(int i=1;i<N;i++) pos[i]=n+1; 10 for(int i=n;~i;i--) { 11 for(int j=1;j<N;j++) nxt[i][j]=pos[j]; 12 pos[a[i]]=i; 13 pre[i][1]=nxt[i][1]; 14 for(int j=2;j<N;j++) pre[i][j]=std::min(nxt[i][j],pre[i][j-1]); 15 } 16 } 17 int main() { 18 scanf("%d",&n); 19 init(n,nxt[0],pre[0],a[0]); 20 scanf("%d",&m); 21 init(m,nxt[1],pre[1],a[1]); 22 scanf("%d",&k); 23 for(int i=0;i<=n;i++) dp[0][i]=m+1; 24 dp[0][0]=0; 25 int *f=dp[0],*g=dp[1]; 26 for(int i=1,l,r,mid;i<=k;i++) { 27 l=1,r=N-1; 28 while(l<r) { 29 bool ok=false; 30 mid=(l+r)>>1; 31 for(int j=0;j<=n;j++) g[j]=m+1; 32 for(int j=0;j<=n;j++) if(f[j]<=m) { 33 int p=pre[0][j][mid]; 34 if(p<=n) g[p]=std::min(g[p],f[j]); 35 g[j]=std::min(g[j],pre[1][f[j]][mid]); 36 } 37 for(int j=0;j<=n&&!ok;j++) if(g[j]<=m) { 38 if(n-j+m-g[j]>=k-i) ok=true; 39 } 40 if(ok) r=mid; 41 else l=mid+1; 42 } 43 ans[i]=r; 44 for(int j=0;j<=n;j++) g[j]=m+1; 45 for(int j=0;j<=n;j++) if(f[j]<=m) { 46 int p=nxt[0][j][r]; 47 if(p<=n) g[p]=std::min(g[p],f[j]); 48 g[j]=std::min(g[j],nxt[1][f[j]][r]); 49 } 50 std::swap(f,g); 51 } 52 int p=1; 53 for(int i=1;i<=n&&p<=k;i++) { 54 if(a[0][i]==ans[p]) p++; 55 } 56 if(p>k) { 57 ans[k]=a[1][1]; 58 for(int i=2;i<=m;i++) ans[k]=std::min(ans[k],a[1][i]); 59 } 60 else { 61 p=1; 62 for(int i=1;i<=m&&p<=k;i++) { 63 if(a[1][i]==ans[p]) p++; 64 } 65 if(p>k) { 66 ans[k]=a[0][1]; 67 for(int i=2;i<=n;i++) ans[k]=std::min(ans[k],a[0][i]); 68 } 69 } 70 for(int i=1;i<=k;i++) printf("%d%c",ans[i]," \n"[i==k]); 71 return 0; 72 }View Code
标签:SDU,return,int,d%,暑期,ans,排位,include,dp 来源: https://www.cnblogs.com/Onlymyheart/p/11260540.html