contest链接:https://codeforces.com/contest/1269
A. Equation
题意:输入一个整数,找到一个a,一个b,使得a-b=n,切a,b都是合数
思路:合数非常多,从1开始枚举b,a就是b+n,每次check一下a,b是否是合数,是的话直接输出,break即可
AC代码:
1 #include<iostream> 2 #include<string> 3 #include<vector> 4 #include<cstring> 5 #include<cstdio> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 typedef long long ll; 10 ll mod = 1e9+7; 11 const int maxn = 2e5+10; 12 bool check(ll x){ 13 for(int i = 2;i<=sqrt(x);i++){ 14 if(x%i == 0) return true; 15 } 16 return false; 17 } 18 int main(){ 19 ll n;cin>>n; 20 ll cur = 1; 21 while(1){ 22 if(check(cur)&&check(cur+n)){ 23 cout<<cur+n<<" "<<cur; 24 return 0; 25 } 26 cur++; 27 } 28 return 0; 29 }
B. Modulo Equality
题意:有两个序列a,b,要求找到一个相对最小的x,让a序列中的所有元素+x再mod m变为序列b,两个序列内部都可以随意交换
思路:可以发现数据范围很小,直接就可以暴力做。首先对a序列和b序列都进行一下从小到大的排序,首先判一下当前的a序列和b序列是否相等,如果相等直接输出0即可。不相等再进行枚举,以b[1]为基准,枚举a[i]中的每个元素,计算一下b[1]和a[i]模运算差值,让a序列所有的元素加上这个差值之后再判断一下是否和b[i]中的每一个元素相等,取最小的x即可。
AC代码:
#include<iostream> #include<string> #include<vector> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; ll mod = 1e9+7; const int maxn = 2e5+10; int main(){ ll cur = 1; ll n,m;cin>>n>>m; ll a[2005],b[2005]; for(int i = 1;i<=n;i++) cin>>a[i]; for(int j = 1;j<=n;j++) cin>>b[j]; sort(a+1,a+n+1); sort(b+1,b+n+1); int f = 0; for(int i = 1;i<=n;i++){ if(a[i]!=b[i]) f = 1; } if(f == 0) { cout<<0; return 0; } ll ans = 1e9+5; ll c[2005]; for(int i = 1;i<=n;i++){ ll x = b[1]>a[i]?b[1]-a[i]:b[1]+m-a[i]; for(int j = 1;j<=n;j++){ c[j] = (a[j]+x)%m; } sort(c+1,c+n+1); ll f = 0; for(int j = 1;j<=n;j++){ if(c[j]!=b[j]) { f = 1; break; } } if(f == 0){ ans = min(ans,x); } } cout<<ans; return 0; }
C. Long Beautiful Integer
题意:给一个长度为n(长度数据范围2e5)的数字,要求把n转化为大于n且尽可能小的数,使得新的数字每一位 bi = bi+k。
思路:首先对数的前k位进行存储,第k位之后,每一位的数字都受到前k位影响,因为前k位一旦有变换,k位之后必定也需要改变才能满足bi = bi+k,那么只需要对前k位进行枚举即可,每次让前k位组成的数字+1,再变换k位之后的数字,如果这个数字大于最初的n,那么就是break,已经是最小的答案了。同时需要注意一下进位的问题,比如19999,+1之后变为20000,这个也需要处理一下。
AC代码:
#include<iostream> #include<string> #include<vector> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; ll mod = 1e9+7; const int maxn = 2e5+10; int main(){ ll n,k;cin>>n>>k; string s; cin>>s; string ans = s; string ak = ""; for(int i = 0;i<k;i++){ ak+=s[i]; } for(int i = k;i<n;i++){ ans[i] = ak[i%k]; } for(int i = k;i<n;i++){ if(s[i]<ans[i]){ break; } if(s[i] == ans[i]){ continue; } if(s[i]>ans[i]){ int cur = k - 1; while(ak[cur] == '9'){ ak[cur] = '0';//处理进位,让所有的9变为0,遇到第一个不是9的让其+1即可 cur--; } ak[cur] = ak[cur] + 1; break; } } for(int i = 0;i<n;i++){ ans[i] = ak[i%k]; } cout<<ans.size()<<endl; cout<<ans; return 0; }
D. Domino for Young
题意:给一个不规则的网格,在上面放置多米诺骨牌,多米诺骨牌长度要么是1x2,要么是2x1大小,问最多放置多米诺骨牌的数量。
思路:首先这是一个结论题,对每个方格进行染色,一个方格染黑色,周围邻近的就染白色,答案就是黑色方格数量和白色方格数量的最小值。这个结论可以用二分图进行证明:把问题抽象成最大二分图匹配,每两个点之间连一条边。一个格子和周围格子连一条边,如果一个格子周围的还没被匹配,那么匹配数+1。如果一个格子周围已经全部被匹配完,那么该格子可以增广到任意一个为匹配位置,匹配数+1。所以只要在另一种颜色格子数量充沛的情况下,每一次匹配都能对匹配数贡献1,所以答案就是两种颜色的最小值。
AC代码:
#include<iostream> #include<string> #include<vector> #include<cstring> #include<cstdio> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; ll mod = 1e9+7; const int maxn = 2e5+10; int main(){ ll black = 0 , white = 0; int n;cin>>n; int cur = 0; for(int i = 0;i<n;i++){ ll a ;cin>>a; if(cur == 0){ black +=(a/2+a%2); white +=a/2; cur = 1; } else{ cur = 0; black +=a/2; white +=(a/2+a%2); } } ll ans = min(black,white); cout<<ans; return 0; }
E. K Integers
题意:给一个序列P1,P2,P3,P4....Pi,每次可以交换两个相邻的元素,执行最小次数的交换移动,使得最后存在一个子段1,2,…,k,这是题目所定义的f(k),题目要求求出所有的f(n),并依次输出。
思路:首先考虑逆序对问题,比如3 2 1 4这个序列,要使其变为1 2 3 4,最小的移动次数是这个序列中逆序对之和,2+1 = 3,逆序对是(3,2) (3,1)(2,1),但是在比如序列3 5 2 1 6 7 4 8 9,求f(4)怎么做?首先是不是把1 2 3 4这个序列聚成在一起,相连在一起,再去计算逆序对个数,两个过程所花费相加就是答案。那么这个题目就分为两个过程,1.聚合n个数字在一起。2.求逆序对的个数,两者花费相加就行。第1个过程如果使得聚合步数最少呢?其实就是求出聚合后的最中间的位置,其他所有的数字向这个位置靠近所花费的移动次数是最少的,这个过程可以用二分做。第2个过程可以用树状数组,也可以用线段树做。输入的时候记录每个数字的位置,建两个树状数组,一个树状数组维护数字出现的次数,用来求逆序对个数,另一个树状数组维护各个数字在原序列的位置。
1 #include<iostream> 2 #include<string> 3 #include<vector> 4 #include<cstring> 5 #include<cstdio> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 typedef long long ll; 10 ll mod = 1e9+7; 11 const int maxn = 2e5+10; 12 ll t[maxn],cnt[maxn]; 13 ll pos[maxn]; 14 int n; 15 inline int lowbit(ll x){ 16 return x&(-x); 17 ///算出x二进制的从右往左出现第一个1以及这个1之后的那些0组成数的二进制对应的十进制的数 18 } 19 void add(ll *b , int x, int k) {//单点修改 20 while (x <= n) { //不能越界 21 b[x] = b[x] + k; 22 x = x + lowbit(x); 23 } 24 } 25 ll getsum(ll *b,int x) { // a[1]……a[x]的和 26 ll ans = 0; 27 while (x > 0) { 28 ans = ans + b[x]; 29 x = x - lowbit(x); 30 } 31 return ans; 32 } 33 int main(){ 34 ios::sync_with_stdio(false); 35 cin.tie(0); 36 cin>>n; 37 for(int i = 1;i<=n;i++){ 38 int t; 39 cin>>t; 40 pos[t] = i;//记录t的位置 41 } 42 ll inv = 0;//记录逆序对的个数 43 for(int i = 1;i<=n;i++){ 44 inv += (i-1-getsum(t,pos[i]));//每次统计逆序对的个数 ,累加即可 45 add(t,pos[i],1);//在t数组上的pos[i]位置上+1 46 add(cnt,pos[i],pos[i]);// cnt数组上维护所有数组的位置, 47 if(i==1){ 48 cout<<0<<" "; 49 continue; 50 } 51 int mid,l = 1,r = n; 52 while(l<=r){//二分枚举中最中间的位置,所有数字向这个位置靠近 53 mid = (l+r)>>1; 54 if(getsum(t,mid)*2<=i){ 55 l = mid+1; 56 } 57 else{ 58 r = mid-1; 59 } 60 } 61 ll ans = 0; 62 ll cntL = getsum(t,mid);//cntL在mid左边需要的数字个数之和 63 ll cntR = i - cntL;//cntR是mid右边需要的数字个数之和 64 ll indexL = getsum(cnt,mid);//mid左边需要数字的位置之和 65 ll indexR = getsum(cnt,n)-indexL;//mid右边需要数字的位置之和 66 ans+=((mid+(mid-cntL+1))*cntL)/2-indexL;//累加mid左边数字靠近邻近mid位置所需要的移动次数 67 ans+=(indexR-((mid+1+(mid+cntR))*cntR)/2);//累加mid右边数字靠近邻近mid位置所需要的移动次数 68 cout<<ans+inv<<" ";//逆序对+聚合移动次数为答案 69 } 70 return 0; 71 }