UVALive - 4850 Installations
飞机票:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16762
题意:给你n个服务,然后每个服务有时间di,和执行时间si,如果执行后的时间超过di,则惩罚值为max(0,最终的时间-di),求最大两个惩罚值相加最小。
分析:二分套二分套二分,复杂度logn*n*logn*n。。。ORZ
第一个二分是确定两个和最小值用的,假设当前检测的值是x,则我们枚举每个服务,然后第二个二分,二分x的值,且l要大于等于x/2向上取整,r为x,这个是为了确定两个其中一个比较大的值,那么另一个值就可以为x-大的值,然后第三个二分,用来确定k应该插入到哪里去,假设大的值为i,小的值为j,则每个服务的d值都得家j,但是k的d值要加i,应该能理解,然后不是属于k的我们只要排序一次就够了,因为加了跟没加排序不变,然后用第三个二分把k插进去,按更新后的d从小到大,why??因为d越大的话,它的可用区域越大,贪心~所以把小的先处理,如果当前执行的任务超过了当前的d,如果是k,那就是说明k更新的值太小,所以第二个二分的l=mid+1,如果不是k,且超过了,那个r=mid-1,k太大~,如果可以,那么就说明当前方案是可以的,更新第一个二分。。。最后输出,over,啪啪啪,AC~
/* Author:Chieh Because of You */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <vector> #include <stack> #define LL long long using namespace std; const int maxn=5*123; struct he { int s,d; } JLJ[maxn]; int T,n; bool cmp(he a,he b) { return a.d<b.d; } void init() { scanf("%d",&n); for(int i=1; i<=n; i++)scanf("%d%d",&JLJ[i].s,&JLJ[i].d); } vector<he> V; int isOK(int x,int y,int idx) { int l=0; int r=V.size()-1; while(l<=r) { int mid=(l+r)/2; if(V[mid].d+y>JLJ[idx].d+x) { r=mid-1; } else { l=mid+1; } } V.insert(V.begin()+l, JLJ[idx]); int ip=l; int before=0; for(int i=0; i<V.size()-1; i++) { int tm=y; if(i==ip)tm=x; int s=V[i].s; int d=V[i].d+tm; if(s+before>d) { V.erase(V.begin()+ip); if(i==ip)return 1; return 2; } else before+=s; } V.erase(V.begin()+ip); return 0; } bool check(int x) { for(int i=1; i<=n; i++) { V.erase(V.begin()+i-1); int l=x/2; int r=x; if(x%2!=0)l++; while(l<=r) { int mid=(l+r)/2; int t=isOK(mid,x-mid,i); if(t==0) { V.insert(V.begin()+i-1,JLJ[i]); return 1; } if(t==1) { l=mid+1; } else { r=mid-1; } } V.insert(V.begin()+i-1,JLJ[i]); } return 0; } void play() { sort(JLJ+1,JLJ+1+n,cmp); V.clear(); for(int j=1; j<=n; j++) { V.push_back(JLJ[j]); } he a; a.s=0; a.d=2000000000; V.push_back(a); int l=0; int r=1000000; int MIN=1000000000; while(l<=r) { int mid = (l + r) / 2; if(check(mid)) { MIN=min(mid,MIN); r=mid-1; } else { l=mid+1; } } printf("%d\n",MIN); } int main() { scanf("%d",&T); while(T--) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
UVALive - 4725 Airport
飞机票:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=17264
题意:就是给你两个轨道,分别为W,E然后有n个时刻,每个时刻回来pi和qi个飞机分别到W,E,且每个时刻能从W,E中起飞一辆飞机,但是刚飞到的飞机不能起飞。且每个机场当前的飞机编号为0~飞机数量-1。求两个机场一起最大编号的最小值。
分析:我的想法是二分+贪心+树状数组。
二分是为了求最小值,贪心和树状数组主要是判断用的。二分的边界就不用说了,我是直接用了l=1,r=100000,因为每个机场飞机最多的时候就只有100000,不过答案求出来要减一个1才行,因为是0开始的开始进入主题了;
假设当前判断的速度是x,则我们循环累计每个机场的数量,且每次存入树状数组(后面logn有用)。假设W机场每个时刻飞来ai两飞机。则当前W机场有sum(1。。。k);假设我sum[0],如果sum[0]>x了,那就说明前面的飞机必须起飞need=sum[0]-x辆了,然后就用到了贪心思想:因为每个时刻只能起飞一辆飞机,所以我们从1开始,枚举。。。而且我们用一个vis数组记录当前是否有飞机起飞过,有的话,就继续向后。当当前时间是m时。则我们可以用树状数组求的前面的还有没有飞机,如果有,我们就可以起飞,然后树状树状数组减1、、、如果当m=k的时候还不行说明方案不可行,直接返回false,如果可以那么继续循环,一直到最后。。。如果可以则返回true 更新最小值、、、这里哪里用了贪心呢,就是sum那里大于x时,因为我们从前面开始取的话,这样后面大于x时的选择空间更大。。。这样就可以了,复杂度为log(100000)*(n+2*n*logn)就是logn^2*n了吧,速度还是ok的,啪啪啪就可以AC了
/* Author:Chieh Because of You */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define LL long long using namespace std; const int maxn=5*1234; int C[2][maxn]; int lowbit(int x) { return x&(-x); } int sum(int n,int idx) { int sum=0; while(n>0) { sum+=C[idx][n]; n=n-lowbit(n); } return sum; } int n,T; void change(int i,int x,int idx) { while(i<=n) { C[idx][i]=C[idx][i]+x; i=i+lowbit(i); } } struct he { int a,b; } JLJ[maxn]; void init() { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d%d",&JLJ[i].a,&JLJ[i].b); } } bool vis[maxn]; int summ[2]; int s[2]; bool check(int x) { memset(vis,0,sizeof(vis)); memset(C[0],0,sizeof(C[0])); memset(C[1],0,sizeof(C[1])); summ[0]=summ[1]=0; s[0]=s[1]=1; for(int i=1; i<=n; i++) { for(int j=0; j<=1; j++) { int tp=JLJ[i].a; if(j==1) { tp=JLJ[i].b; } summ[j]+=tp; if(summ[j]>x) { int need=summ[j]-x; int st=1; while(st<=need) { if(s[j]==i)return 0; if(vis[s[j]]) { s[j]++; } else { int p=sum(s[j],j); if(p>0) { change(s[j],-1,j); st++; vis[s[j]]=1; s[j]++; } else { s[j]++; } } } summ[j]=x; } change(i,tp,j); } } return 1; } void play() { int l=1,r=100000; int MIN=r; while(l<=r) { int mid=(l+r)/2; if(check(mid)) { MIN=min(MIN,mid); r=mid-1; } else { l=mid+1; } } printf("%d\n",MIN-1); } int main() { scanf("%d",&T); while(T--) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
UVALive - 4254 Processor
飞机票: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=21663
题意:就是给你n个需要处理的事情,第i个必须在li~ri完成,且数量是wi,完成时最大的速度最小是多少;
分析:求最大的速度最小我们可以使用二分来选择速度,但是怎么判断呢?果然还是太菜;其实可以根据时间来判断;具体怎么判断,时间递增,假设当前是j,则把所有左端点小于等于j的都压入到队列中,且按右端点排序(why?因为右端点越大我们可以在后面处理,所以这里贪心的想法),然后设当前还可以处理now个任务,当前碰到的是i,且任务数是w,我们可以判断下now和i的大小,把now和i都减去小的那个,如果now==0了,则退出了,因为不能再处理任务了,如果w不等于0则还没处理完,在压入到队列里。。。没一次j处理玩,可以判断下,如果最小的那个没处理完的r<=j则表明速度太小,返回false(why??)因为下面大于j的时候已经不能处理它了,如果队列空了,且处理完了那么返回true,你懂得。。。。起先没有想到,,,太渣了。还是看别人的才反映过来。。。啪啪啪,就可以AC了~
/* Author:Chieh Because Of You */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <stack> #include <vector> #define LL long long using namespace std; const int maxn=12345; struct he { int l,r,w; bool operator <(const he &a)const { return r > a.r; } } JLJ[maxn]; priority_queue<he> q; bool cmp(he a,he b) { return a.l<b.l; } int n; void init() { scanf("%d",&n); for(int i=1; i<=n; i++) { scanf("%d%d%d",&JLJ[i].l,&JLJ[i].r,&JLJ[i].w); } } bool check(int x) { int i = 1, j = 0; while (!q.empty()) q.pop(); while (1) { while (i <= n && JLJ[i].l <= j) q.push(JLJ[i++]); int now = x; while (now != 0 && !q.empty()) { he temp = q.top(); q.pop(); int m = min(now,temp.w); now -= m; temp.w -= m; if (temp.w != 0) q.push(temp); } j++; if (!q.empty() && q.top().r <= j) return false; if (q.empty() && i ==n+1) return true; } } void play() { sort(JLJ+1,JLJ+1+n,cmp); int l=1; int r=1000000000; int MIN=r; while(l<=r) { int mid=(l+r)/2; if(check(mid)) { r=mid-1; MIN=min(mid,MIN); } else { l=mid+1; } } printf("%d\n",MIN); } int T; int main() { scanf("%d",&T); while(T--) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
UVALive - 3902 Network
飞机票: http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=16451
题意:就是给你一棵树,给你一个服务器,求在其余非叶子节点上放服务器,使任意的叶子节点到最近服务器的距离不超过k
分析:可以根据树形的性质来进行解决
具体步骤:用pair来存数据,如果first是1就是说有叶子节点没有服务器,如果first是2就说有服务器在;所以当我们搜到叶子节点的时候,返回(1,1),当搜到父子节点,我们对返回的子节点进行贪心选择,如果是1就选择距离最大的没有服务器的叶子节点,如果是2就选择最近的服务器,如果1+2的距离(1,为叶子节点最远距离,2为服务器最近距离)<=k那么就是可以访问到,则返回2,且服务器的距离要加1;如果>k那么就是说访问不到,那么我们要判断,如果1==k则必须架服务器了,不加则不满足条件,如果<k则返回叶子节点距离加1.一直递归到s节点,且s节点本身的2为0。。。ok复杂度为O(n-1)即边的数量;
啪啪啪。就可以AC了
/* Author:Chieh Grow up happy */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <stack> #include <vector> #define LL long long using namespace std; const int maxn=1234; vector<int> V[maxn]; int n,T,s,k; void init() { scanf("%d%d%d",&n,&s,&k); for(int i=1; i<=n; i++)V[i].clear(); int u,v; for(int i=1; i<n; i++) { scanf("%d%d",&u,&v); V[u].push_back(v); V[v].push_back(u); } } int MIN; pair<int,int> DFS(int now,int fa) { int a1=-1; int a2=-1; if(now==s)a2=0; for(int i=0; i<V[now].size(); i++) { int v=V[now][i]; if(v==fa)continue; pair<int,int> p= DFS(v,now); if(p.first==1) { a1=max(a1,p.second); } else { if(a2==-1)a2=p.second; else a2=min(a2,p.second); } } if(a1==-1&&a2==-1) { return make_pair(1,1); } if(a1!=-1&&a2!=-1) { if(a1+a2<=k) { return make_pair(2,a2+1); } else { if(a1==k) { MIN++; return make_pair(2,1); } return make_pair(1,a1+1); } } if(a1==-1) { return make_pair(2,a2+1); } if(a1==k) { MIN++; return make_pair(2,1); } return make_pair(1,a1+1); } void play() { MIN=0; DFS(s,0); printf("%d\n",MIN); } int main() { scanf("%d",&T); while(T--) { init(); play(); } return 0; }