TJUOJ4168解題報告
阿新 • • 發佈:2017-07-12
too name num 二進制位 如果 sin algo sign pac
題目地址:
http://acm.tju.edu.cn/toj/showp4168.html
題目概述:
給出一個集合以及一個m,集合中一開始有n個數,要求加入最少的數使得這個集合的各個子集的和能夠覆蓋1~m。
例如集合A={1,3},m=6,這時只需要將2加入A中,A的所有子集為[1], [2], [3], [1,2], [1,3], [2,3], [1,2,3],他們的和分別是1,2,3,3,4,5,6,能夠覆蓋1~6這個區間,所以答案為1。
大致思路:
一開始想到二進制位上去了,覺得每個數的二進制位都有個貢獻之類的,結果還是too naive啊,跟正解差的太多。
首先對子集A的元素排個序,然後假設當前子集中最大的數為a[i-1],能夠覆蓋的範圍是1~mx,那麽如果a[i]>mx+1的話,必須要插入mx+1這個數才能使覆蓋的範圍仍舊連續,而插入mx+1,會使得覆蓋的範圍變成2*mx+1,此時再比較a[i]與當前的mx的大小,直到a[i]<=mx+1時加入a[i]會使得覆蓋的範圍變成mx+a[i],一直這麽考慮下去就ok。
復雜度分析:
首先對於數m來說,最多只需要log2(m)個數就可以滿足題意了,那麽插入的復雜度應該就是O(n+logm),之前還需要排序,就是O(nlogn),總的的復雜度即O(nlogn)了。
代碼:
#include <iostream> #include <cstdio> #include <cstdlib> #include <cmath> #include <vector> #include <ctime> #include <map> #include <assert.h> #include<stack> #include <set> #include <queue> #include <cstring> #include <algorithm> using namespace std; #define sacnf scanf #define scnaf scanf #define maxn 100010 #define maxm 20010 #define inf 1061109567 #define INF 0x3f3f3f3f #define Eps 0.000001 const double PI=acos(-1.0); #define mod 1000000007 #defineMAXNUM 10000 #define For(i,j,k) for(int (i)=(j);(i)<=(k);(i)++) #define mes(a,b) memset((a),(b),sizeof(a)) typedef long long ll; typedef unsigned long long ulld; void Swap(int &a,int &b) {int t=a;a=b;b=t;} ll Abs(ll x) {return (x<0)?-x:x;} int a[maxn]; int main() { //freopen("data.in","r",stdin); //freopen("data.out","w",stdout); //clock_t st=clock(); int T;scanf("%d",&T); while(T--) { int n;ll m;scanf("%d%lld",&n,&m); For(i,1,n) scanf("%d",&a[i]); sort(a+1,a+1+n);ll ms=0;int cnt=0; for(int i=1;i<=n;i++) { if(ms>=m) break; while(a[i]>ms+1&&ms<m) cnt++,ms=ms*2+1; ms=ms+a[i]; } while(ms<m) cnt++,ms=ms*2+1; printf("%d\n",cnt); } //clock_t ed=clock(); //printf("\n\nTime Used : %.5lf Ms.\n",(double)(ed-st)/CLOCKS_PER_SEC); return 0; }
TJUOJ4168解題報告