联赛前(伪)数据结构专题总结
[Scoi2010]游戏
时间限制: 1 Sec 内存限制: 128 MB
Description
Input
Output
Sample Input
1 2
3 2
4 5
Sample Output
HINT
【数据范围】
对于30%的数据,保证N < =1000
对于100%的数据,保证N < =1000000
题解
在yzh大佬提醒下发现这题几乎和好几个月以前做过的一道二分图题意完全一样,但是二分图的复杂度是O(nm)的,这题理论上讲绝对过不了,但是用了十分神奇的卡常方法之后却真的能过。
正解是并查集,把每个装备看作一条边,把属性看成点。一个环里面一定都能满足,一棵树里只有一个点不满足。用并查集维护点的连通性,如果一个联通块里有点数-1条边就让最大的不满足,通过标记集合来确定有多少点是可行的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj=1000010; const int sj1=10005; int n,h[sj+sj1],e,jg,l[sj+sj1],a1,a2,f[sj+sj1],id; struct B { int ne,v; }b[sj<<1]; void add(int x,int y) { b[e].ne=h[x],b[e].v=y,h[x]=e++; } bool find(int x) { for(int i=h[x];i!=-1;i=b[i].ne) { e=b[i].v; if(f[e]!=id) { f[e]=id; if(l[e]==-1||find(l[e])) { l[e]=x; return 1; } } } return 0; } int main() { scanf("%d",&n); memset(h,-1,sizeof(h)); memset(l,-1,sizeof(l)); for(int i=1;i<=n;i++) { scanf("%d%d",&a1,&a2); add(a1,sj1+i),add(a2,sj1+i); } for(int i=1;i<=sj1;i++) { id=i; if(find(i)) jg++; else break; } printf("%d",jg); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstring> #include<cstdio> using namespace std; const int sj=10005; int n,a1,a2,f[sj]; bool v[sj]; int find(int x) { if(f[x]==-1) return x; f[x]=find(f[x]); return f[x]; } void hb(int x,int y) { if(x>y) swap(x,y); f[x]=y; if(!v[x]) v[x]=1; else v[y]=1; } int main() { scanf("%d",&n); memset(f,-1,sizeof(f)); for(int i=1;i<=n;i++) { scanf("%d%d",&a1,&a2); a1=find(a1),a2=find(a2); if(a1==a2) v[a1]=1; else hb(a1,a2); } for(int i=1;i<=sj;i++) if(!v[i]) { printf("%d",i-1); break; } return 0; }
JC loves Mkk
时间限制: 1 Sec 内存限制: 64 MB
输入
第1行,包含三个整数。n,L,R。
第2行n个数,代表a[1..n]。
输出
仅1行,表示询问答案。
如果答案是整数,就输出整数;否则,输出既约分数“P/Q”来表示。
样例输入
5 3 4
3 1 2 4 5
样例输出
7/2
HINT
1≤L≤R≤n≤10^5,0≤ai≤10^9,保证问题有解,数据随机生成
题解
神奇的二分……我明知道平均值问题我只会二分,但是看到输出精确结果还是很不敢想,初次之外又一筹莫展。确实是需要实数二分,精度问题需要long double。把每一个a[i]都减去mid,那么问题就在于找到一个满足条件的区间,使区间和大于0。区间和转化为前缀和是司空见惯的,环拆成链也是自然。怎样确保选偶数个呢?维护一个单调队列,在奇数中和偶数中各找递增的前缀和。i和r作为维护单调队列的标准,每次检查是否有递增的前缀和,即满足条件的区间和,这两个前缀和之间的差就是我们需要的答案的分母。分子/分母=二分结果,通过分母×二分结果向上取整得到整数形式的分子。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int sj=200010; const long double eps=1e-6; int n,l,r,q[2][sj],h[2],t[2]; long long fz,fm,a[sj],gc; long double mid,zj,yj,sum[sj]; bool op; bool check(long double x) { for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-x; h[1]=h[0]=t[1]=t[0]=1; for(int i=l;i<=n;i++) { int j=i-l,op=i&1; while(h[op]<t[op]&&sum[j]-sum[q[op][t[op]-1]]<0) t[op]--; q[op][t[op]++]=j; if(q[op][h[op]]<i-r) h[op]++; if(sum[i]-sum[q[op][h[op]]]>0) { fm=i-q[op][h[op]]; return 1; } } return 0; } long long gcd(long long x,long long y) { if(y==0) return x; return gcd(y,x%y); } int main() { scanf("%d%d%d",&n,&l,&r); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); if(a[i]>yj) yj=a[i]; } for(int i=1;i<n;i++) a[i+n]=a[i]; l+=l&1,r-=r&1,n<<=1; while(zj<yj-eps) { mid=(zj+yj)/2; if(check(mid)) zj=mid; else yj=mid; } mid=(zj+yj)/2; fz=(long long)(mid*fm+0.5); gc=gcd(fz,fm); fz/=gc,fm/=gc; printf("%lld",fz); if(fm!=1) printf("/%lld",fm); return 0; }
[Usaco2013 Open]Photo
时间限制: 1 Sec 内存限制: 128 MB
题目描述
Farmer John has decided to assemble a panoramic photo of a lineup of his N cows (1 <= N <= 200,000), which, as always, are conveniently numbered from 1..N. Accordingly, he snapped M (1 <= M <= 100,000) photos, each covering a contiguous range of cows: photo i contains cows a_i through b_i inclusive. The photos collectively may not necessarily cover every single cow. After taking his photos, FJ notices a very interesting phenomenon: each photo he took contains exactly one cow with spots! FJ was aware that he had some number of spotted cows in his herd, but he had never actually counted them. Based on his photos, please determine the maximum possible number of spotted cows that could exist in his herd. Output -1 if there is no possible assignment of spots to cows consistent with FJ"s photographic results.
给你一个n长度的数轴和m个区间,每个区间里有且仅有一个点,问能有多少个点
输入
* Line 1: Two integers N and M.
* Lines 2..M+1: Line i+1 contains a_i and b_i.
输出
* Line 1: The maximum possible number of spotted cows on FJ"s farm, or -1 if there is no possible solution.
样例输入
5 3
1 4
2 5
3 4
INPUT DETAILS: There are 5 cows and 3 photos. The first photo contains cows 1 through 4, etc.
样例输出
1
OUTPUT DETAILS: From the last photo, we know that either cow 3 or cow 4 must be spotted. By choosing either of these, we satisfy the first two photos as well.
题解
久违的单调队列优化dp!题目叙述有点像小暑假考试的《守卫》,但是那题我至今没有改出来,而且《守卫》凡是没有看到的地方都可能有,和这题有所区别。还是就题论题吧,每一个区间里必须有点,所以从某一点向前最远到不包括它的一个区间左端点至少要有一个点,这是可行解的左端点;每个区间里只有一个,所以从包含它的区间左端点再向左才有点,这是可行解的右端点。左端点取max,右端点取min,我们就得到了可行的转移区域。对于每个点来说可行区域是单调向右滑动的,可以用单调队列优化。对于每个点,我们把队列更新到r[i],再把l[i]之前的删除出队列,就可以取队首转移了。f[i]=max{f[j],l[i]<=j<=r[i]}+1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<iostream> using namespace std; const int sj=200010; int n,m,f[sj],q[sj],h,t,l[sj],r[sj],a1,a2,ans,te; void getlr() { for(int i=1;i<=n+1;i++) r[i]=i-1; for(int i=1;i<=m;i++) { scanf("%d%d",&a1,&a2); if(a1>l[a2+1]) l[a2+1]=a1; if(a1-1<r[a2]) r[a2]=a1-1; } for(int i=2;i<=n+1;i++) l[i]=max(l[i-1],l[i]); for(int i=n;i>=1;i--) r[i]=min(r[i+1],r[i]); } int main() { scanf("%d%d",&n,&m); getlr(); h=t=te=1,q[1]=0; for(int i=1;i<=n+1;i++) { while(te<=r[i]) { if(f[te]==-1) { te++; continue; } while(h<=t&&f[q[t]]<f[te]) t--; q[++t]=te; te++; } while(h<=t&&q[h]<l[i]) h++; if(h<=t) { f[i]=f[q[h]]+1; if(i==n+1) f[i]--; } else f[i]=-1; } printf("%d",f[n+1]); return 0; }