contest链接:https://codeforces.com/contest/1288
A. Deadline
题意:略
思路:根据题意 x + [d/(x+1)] 需要找到一个x使得上式小于等于n,即x + [d/(x+1) ] <=n,不等式两边同时+1得 x+1 + [d/(x+1)] <=n + 1当且仅当(x+1)2 = d时,式子左边最小,所有只需要判断一下最小值是否<=n+1就可以知道该不等式是否存在x满足题意了,即找到x = √d - 1,判断一下即可。
AC代码:
1 #include<iostream> 2 #include<vector> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<set> 8 #include<cstring> 9 #include<queue> 10 #include<map> 11 using namespace std; 12 typedef long long ll; 13 int main(){ 14 int t; 15 cin>>t; 16 while(t--){ 17 ll n,d; 18 cin>>n>>d; 19 if(d<=n){ 20 cout<<"YES"<<endl; 21 continue; 22 } 23 ll t = sqrt(d) - 1; 24 ll ans = t + (d/(t+1)); 25 if(d%(t+1)!=0) ans++; 26 if(ans<=n){ 27 cout<<"YES"<<endl; 28 } 29 else{ 30 cout<<"NO"<<endl; 31 } 32 } 33 return 0; 34 }
B. Yet Another Meme Problem
题意:题目定义了一个公式,a×b + a + b = conc(a,b),给出a,b的取值范围,求满足此公式条件的所有pari(a,b)个数
思路:只有当b = 9,99,999....9999999的时候,a为任意值,才满足以上等式,然后判断一下b范围内覆盖的9,99,999...........9999999999即可
AC代码:
1 #include<iostream> 2 #include<vector> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<set> 8 #include<cstring> 9 #include<queue> 10 #include<map> 11 using namespace std; 12 typedef long long ll; 13 int main(){ 14 int t; 15 cin>>t; 16 while(t--){ 17 ll a,b; 18 cin>>a>>b; 19 ll tb = b; 20 int cnt = 0; 21 while(b){ 22 b/=10; 23 cnt++; 24 } 25 ll s = pow(10,cnt)-1; 26 // cout<<s<<endl; 27 if(tb != s) cnt--; 28 ll ans = a*cnt; 29 cout<<ans<<endl; 30 } 31 return 0; 32 }
C. Two Arrays
题意:给定一个数n和一个数m,让构建两个数组a和b满足条件,1.数组中所有元素的取值在1~n之间,a和b数组长度是m。2. a数组是单调不递减的,b数组是单调不递增 3. 任意的位置i,有ai<=bi
思路:可以组合数学做,也可以dp,以下为dp做法。首先如果把a、b两个数组合并成 a1,a2,a3,.......am,bm,bm-1,bm-2,bm-3...........b3,b2,b1,会发现整个数列是单调不递减的,那么就可以dp做了,
dp[i][j]表示第i个位置可以放 大于等于 j 的方案数 ,那么转移方程就是 dp[i][j] = dp[i-1][j] + dp[i][j+1]
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int maxm = 12; 10 const int maxn = 1e3+5; 11 const int mod = 1e9+7; 12 ll dp[maxm*2][maxn]; 13 int main(){ 14 int n,m; 15 scanf("%d%d",&n,&m); 16 for(int i = 1;i<=n;i++) dp[1][i] = 1; 17 for(int i = 2;i<=2*m;i++){ 18 for(int j = n;j>=1;j--){ 19 dp[i][j] = (dp[i][j+1] + dp[i-1][j])%mod; 20 } 21 } 22 ll ans = 0; 23 for(int i = 1;i<=n;i++){ 24 ans = (ans+dp[2*m][i])%mod; 25 } 26 printf("%d",ans); 27 return 0; 28 }
D. Minimax Problem
题意:给定n个数组,长度为m,从n中数组挑选两个数组,两个数组中的每一位取两者的最大值组成一个新的数组,新数组中的最小值记为c,所有组合中c的最大值
思路:思路:题目中m的范围只有8,数组中元素的范围是1e9,显然m的范围非常特殊,似乎可以把数组用二进制的形式进行状态压缩,这里采用二分答案的方法。二分范围是数组元素最小值到最大值,每次check(mid)一遍,check的时候对于每个数组,用二进制的形式表示出来,数组中小于mid的值用0表示,大于等于mid的用1表示,此时所有的数组都进行了二进制转化,比如一个数组1 2 3 4 5,mid = 3,然后数组就可以状态压缩为0 0 1 1 1 。然后去枚举这些二进制,这样其实最多只会枚举2m × 2m 次,如果枚举的两组二进制相或为11111111,那么说明找到了一组解,返回继续二分,如此过程时间复杂度只有O(n×m + 2m×2m),m的范围只有8。具体看代码
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int maxn = 3e5+5; 10 int n,m; 11 int cnt[maxn]; 12 int a[maxn][10]; 13 int num[270]; 14 int ans1,ans2; 15 bool check(int cur){ 16 memset(num,0,sizeof(num));//初始化num数组 17 for(int i = 1;i<=n;i++){ 18 int x = 0; 19 for(int j = m - 1;j>=0;j--){ 20 if(a[i][j]>=cur) x+=(1<<j);///统计满足a[i][j]>=cur的数组的每一位 21 } 22 num[x] = i;//每个数组都转化为二进制 23 } 24 for(int i = 0;i<(1<<m);i++){ 25 for(int j = 0;j<(1<<m);j++){ 26 if(num[i]!=0 && num[j]!=0 && (j|i) == (1<<m)-1){//枚举二进制形式的所有数 27 ans1 = num[i]; 28 ans2 = num[j]; 29 return true; 30 } 31 } 32 } 33 return false; 34 } 35 int main(){ 36 int r = -1,l = 1e9+10; 37 scanf("%d%d",&n,&m); 38 for(int i = 1;i<=n;i++){ 39 for(int j = 0;j<m;j++){ 40 scanf("%d",&a[i][j]); 41 r = max(r,a[i][j]); 42 l = min(l,a[i][j]); 43 } 44 } 45 int mid; 46 while(l<r){//二分答案 47 mid = (l+r+1)/2; 48 if(check(mid)) l = mid ; 49 else r = mid - 1; 50 } 51 check(l); 52 printf("%d %d",ans1,ans2); 53 return 0; 54 }
E. Messenger Simulator
题意:序列p的长度为n,初始序列为1 2 3 4 ...n,然后有m次操作,每次指定序列中一个数移动到第一位,然后剩下的所有序列往后移动一位,求每个数在出现过的所有历史序列中所在位置索引的最大值和最小值。
思路:用一个树状数组维护序列的位置,在序列的前面空出m个位置,目的是留给m次操作移动数字到前m个位置。初始时,在输入数据的时候,用pos数组记录所有数字的位置为 i+m,然后树状数组的 i+m处更新+1代表第i+m个位置放了一个数,每次移动操作时,在该位置做-1的更新操作表示此处清零,该位置已经没有放置数字,然后可以用树状数组查询该位置前面部分的区间和,就表示前面有多少个数,自然而然就可以更新这个数出现位置的最大值了,而最小值更新则为:如果进行了移动操作,那么该数字位置的最小值就是1了,因为把该数字放在了序列最前面,最后再遍历一遍所有数字,查询更新一些没有进行移动操作的数出现位置的最大值。具体看代码
AC代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int maxn = 3e5+5; 10 int t[maxn*2]; 11 int ansMin[maxn+1],ansMax[maxn+1]; 12 int n,m; 13 inline int lowbit(int x){ 14 return x&(-x); 15 } 16 void add(int x,int k){ 17 while(x<=n+m){ 18 t[x] = t[x] + k; 19 x +=lowbit(x); 20 } 21 } 22 int get(int x){ 23 int ans = 0; 24 while(x>=1){ 25 ans+=t[x]; 26 x-=lowbit(x); 27 } 28 return ans; 29 } 30 int main(){ 31 scanf("%d%d",&n,&m); 32 int pos[n+m+1]; 33 for(int i = 1;i<=n;i++){ 34 pos[i] = i + m;//初始化元素的位置,pos[i]为元素i的位置 35 ansMin[i] = i,ansMax[i] = i; 36 add(i + m,1);//树状数组该位置更新+1 37 } 38 for(int i = 0;i<m;i++){ 39 int temp; 40 scanf("%d",&temp); 41 ansMin[temp] = 1; 42 add(pos[temp],-1);//该位置-1, 43 add(m-i,1);//移动到最前面,树状数组+1 44 ansMax[temp] = max(ansMax[temp],get(pos[temp]));//查询前面有多少个元素,做max的更新 45 pos[temp] = m - i;//更新位置 46 } 47 for(int i = 1;i<=n;i++){ 48 ansMax[i] = max(ansMax[i],get(pos[i]));//最后check没有进行移动操作的元素 49 } 50 for(int i = 1;i<=n;i++){ 51 printf("%d %d ",ansMin[i],ansMax[i]); 52 } 53 return 0; 54 } 55 56 57
b
)