C題
這道題用暴力去寫想都不要想,一定超時,于是我們需要優化,下面是思路過程:
如圖,本題只需找到x的因數個數和(n-x)的因數個數,這兩個相乘,得到的就是對于這個x來說組合的個數,且x的取值為1~n,由題n取不到。
代碼:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int num[N];//記錄各個數字的因數個數
int main(){int n,ans=0;//ans方案種數cin>>n;for(int i=1;i<n;i++){ //遍歷1~n的數字for(int j=1;j*j<=i;j++){ //求其因數個數if(i%j==0){num[i]++; //由題知,A,B,C,D這四個數是有序的,因此每次判斷都會有兩個因數if(j*j!=i)num[i]++; //所以下面要+1,除非這兩個數相等只需加1次}}}for(int i=1;i<n;i++){ //遍歷和為n,加數所有可能的取值ans+=num[i]*num[n-i];//因數個數相乘即得對應的組合數}cout<<ans;return 0;
}
H題
本題乍一看很簡單,就是求出每一個子集的和即可,但如何不重不漏的求出每一個集合我是真的不會,好的,換思路,利用貪心思想,將原集合排序,從第一個最小的數開始,逐步擴展可以表示的子集和范圍,同時找到第一個無法表示的整數。因為已經排好序了,所以對于元素個數相同的集合來說,總是最前面的最小,而且代碼的判斷條件是a[i]>sum+1,那么sum + 1
就無法被表示,因為當前的子集和范圍無法覆蓋到 sum + 1,直接輸出
sum+1,相反,如果a[i]<=sum+1,說明當前子集很可能已經覆蓋了sum+1,讓a[i]與sum+1比較是否可以覆蓋,同時也可以避免出現不同子集因元素個數的不同造成對應的子集和大小不同問題,因為如果a[i]>sum+1說明a[i]一定很大,那么毫無疑問元素多的集合的和一定比元素少的集合的和大,這樣我們就可以實現由小到大逐步擴展子集和范圍。
代碼如下:
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
long long a[N];
int main(){int n;cin>>n;for(int i=0;i<n;i++){cin>>a[i];}sort(a,a+n); //由小到大逐步排序long long sum=a[0]; //記錄當前子集和的最大值for(int i=1;i<n;i++){if(sum+1<a[i]){ //比較當前子集和+1是否可以被下一個集合元素表示出來cout<<sum+1; //如果可以,輸出,結束return 0;}else sum+=a[i]; //否則,繼續累加}cout<<sum+1; //如果一直沒在上面結束的話,此時sum代表全集的和,+1即是答案return 0;
}
D題
對于這道題需要知道一個知識點:
x+y=(x&y)+(x|y)
根據位運算性質,x&y和x|y滿足:(x&y)&(x|y)=x&y
本題思路:
由于x&y=a;說明x>=a,y>=a,則x+y>=2a,即s>=2a——第一個判斷條件
又由x&y=a,x+y=s和上述知識點知x|y=s-a。因此,a&(s-a)=a——第二個判斷條件
如果第一個不滿足的話,直接輸出NO,程序結束,否則,繼續判斷第二個條件。
這樣做的好處是不用一一枚舉出來x,y的值
#include<bits/stdc++.h>
using namespace std;
int main(){int t;long long a,s;cin>>t;while(t--){cin>>a>>s;if(s<2*a){ //首先判斷cout<<"No"<<endl;}else{ //繼續深入判斷if((a&(s-a))==a) cout<<"Yes"<<endl;else cout<<"No"<<endl;}}return 0;
}
比賽地址:https://www.nowcoder.com/acm/contest/110544。【邀請碼:666666】