Codeforces Round #813 (Div. 2) (补题中)
战绩:
A. Wonderful Permutation
签到题。
计算前k个就把最小的那k个转移到前k项,看数组前k项缺多少最小前k项就行,可以在O(k)的复杂度内解决。
int main() { read(T); while(T--) { read(n);read(k); for(int i=1;i<=n;i++) { read(a[i]); b[i]=a[i]; } sort(a+1,a+1+n); int ans=0; for(int i=1;i<=k;i++) { for(int j=1;j<=k;j++) { if(a[i]==b[j]) break; else if(j==k) ans++; } } cout<<ans<<endl; } return 0; }
O(n复杂度代码):应付n和k的限制可以更大
int main() { cin>>T; while(T--) { int n,k; cin>>n>>k; int ans=0; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=k;i++)if(a[i]>k)ans++; cout<<ans<<endl; } }
B. Woeful Permutation
最大的利用每个数字的lcm就是让两个数字没有除了1以外的公因数也就是两两互质,每个数字n和n-1或者n+1是一定互质的。
题目要求最大,我们就从最后一位开始往前,两两交换数字,这样得到的答案一定是最大的。
int main() { read(T); while(T--) { read(n); a[0]=1; for(int i=1;i<=n;i++) a[i]=i; for(int i=n;i>=1;i--) { if(i%2==n%2) swap(a[i],a[i-1]); } for(int i=1;i<=n;i++) cout<<a[i]<<" "; cout<<endl; } return 0; }
C. Sort Zero
给定的数字一定是一组正数,我们每次把一种数字变为0,也就意味着这个数字会变成最小的那个。
当某一位变成0之后,我们很容易发现,为了不递减,前面的所有位置都会变为0,所以对原数组一定是保留最后那一节不递减序列。
可以用set维护,实现很快。
int main() { read(T); while(T--) { st.clear(); read(n); for(int i=1;i<=n;i++) { read(a[i]); b[i]=0; } for(int i=2;i<=n;i++) if(a[i]<a[i-1]) b[i]=1; bool flag=1; for(int i=n;i>=1;i--) { if(!flag) st.insert(a[i]); else if(b[i]==1) flag=0; } flag=1; for(int i=n;i>=1;i--) { if(!flag) st.insert(a[i]); else if(st.find(a[i])!=st.end()) flag=0; } cout<<st.size()<<endl; } return 0; }
有队友试图hack所以多解释两句代码... ...
第一次循环我们保留了最后一节不递减序列,这之前的全部变为0,但是有可能会有和后面数字和前面相同的地方,我们也要变为0,所以再做一遍,将和前面的数字相同的最后一个数字之前变为0.
这个时候新变为0的数字不会再在后面出现,因为我们保留的是一个不递减序列,因此只用再做一次。
D. Empty Graph(图论二分)
比赛做的时候就想的是二分,但是题目打着打着猪脑过载,最后交了发WA的屎山代码不想改了。
后来在纸上慢慢推了下思路,发现代码完全不长。
首先明确题意,两点间的路径是在这两个点的区间内最小点的值,我们最后要有一种修改方式,使得所有的两点间的最短距离的最大值尽可能大。
最小值最大类问题,可以率先想到二分法。
我们修改一定是要把某个数字改成1e9.
我们二分一个答案路径长度,看使得答案符合大于等于这个路径需要最少修改
首先我们确定一个最小值点作为中转点,让2*a[i]<二分的答案的点全部都变为1e9,下面解释
对于修改值后的某两个点(u和v)的路径,其中一定有一条是通过最小的点(x)连接的(u->x->v),又有x是最小值,因此这条路的大小就是2*a[x]。
但是这两个点也可以通过直接相连的路到达(由于x最小,通过其他中转点不会比2*a[x]更优)。
对于一个路径(u->x->v)u和v的直接路径实际上是(u->x和x->v)这两段路的最小值。我们只用看相邻的路径。
我们只要保证图里存在一对相邻点,他们的直接路径大于2*a[x],那么答案就会被固定为2*a[x]。
因为这意味着存在这样的两个点,他们的最短路径通过x连接两个点,长度为2*a[x],其他的路径比它小也不会影响答案。
但是如果不存在这样的两个点,我们必须进行修改。
如果存在相邻的两个点a和b,他们一个符合值大于2*a[x],另一个小于2*a[x],我们修改一次,只用修改小的那个。
不存在这样的a和b则修改两次。
比赛代码改的,有点混乱。
#include<iostream> #include<cstdio> #include<string> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<set> #include<ctime> #define N 150000 #define ll long long using namespace std; ll n,T,k,rec,ans; struct node { ll val,pos; }ed[N],a[N]; inline void read(ll &p) { p=0;ll f=1; char ch=getchar(); while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') p=p*10+(ch-'0'),ch=getchar(); p*=f; } inline bool cmp(node a,node b) {return a.val<b.val;} inline bool cmp2(node a,node b) {return a.pos<b.pos;} inline bool jud(ll x) { ll lim=0; for(int i=1;i<=n;i++) a[i]=ed[i]; for(int i=1;i<=n;i++) { if(2*a[i].val<x){a[i].val=1000000000;lim++;} } if(lim>k) return 0; sort(a+1,a+1+n,cmp2); ll f=2; for(int i=1;i<n;i++) { if(a[i].val>=x&&a[i+1].val>=x) f=0; if((a[i].val<x&&a[i+1].val>=x)||(a[i+1].val<x&&a[i].val>=x)) {f=min(1ll*1,f);} } if(lim+f>k) return 0; else return 1; } int main() { read(T); while(T--) { read(n);read(k); for(int i=1;i<=n;i++) { read(ed[i].val); ed[i].pos=i; } sort(ed+1,ed+1+n,cmp); if(n==k) printf("1000000000\n"); else if(n==2) printf("%lld\n",ed[2].val); else { ans=0; ll l=0,r=1e9; while(l<=r) { ll mid=(l+r)>>1; if(jud(mid)) {ans=mid;l=mid+1;} else r=mid-1; } printf("%lld\n",ans); } } return 0; }