2024省选联测1
题目来源: 2024省选联测1
\(T1\) HZTG5808. interval \(40pts\)
-
原题: QOJ 1173. Knowledge Is..
-
考虑按照左端点升序排序后反悔贪心。
-
分别维护已经匹配的区间对和未被匹配的区间,若当前区间 \(a\) 可以和前面剩余的未被匹配的区间匹配则直接匹配;否则尝试找到一对已经匹配的区间 \(x,y\) ,若 \(y\) 的右端点比 \(a\) 的右端点靠左则替换成 \(x,a\) 进行匹配,可以证明将 \(y\) 换成 \(a\) 扩大了 \(y\) 能匹配的区间(如果不换就是 \(a\) 的匹配区间),一定不劣。
-
优先队列按照右端点升序排序即可。
点击查看代码
pair<int,int>a[500010]; priority_queue<int,vector<int>,greater<int> >used,wait; int main() { #define Isaac #ifdef Isaacfreopen("interval.in","r",stdin);freopen("interval.out","w",stdout); #endifint n,ans=0,i;scanf("%d",&n);for(i=1;i<=n;i++){scanf("%d%d",&a[i].first,&a[i].second);}sort(a+1,a+1+n);for(i=1;i<=n;i++){if(wait.empty()==0&&wait.top()<a[i].first){ans++;used.push(a[i].second);wait.pop();}else{if(used.empty()==0&&used.top()<a[i].second){wait.push(used.top());used.pop();used.push(a[i].second);}else{wait.push(a[i].second);}}}printf("%d\n",ans); return 0; }
-
输出方案时只需要记录下来源后倒着给予编号即可。
点击查看代码
struct node {int l,r,id; }a[500010]; int col[500010]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >used,wait; bool cmp(node a,node b) {return (a.l==b.l)?(a.r<b.r):(a.l<b.l); } int main() { // #define Isaac #ifdef Isaacfreopen("interval.in","r",stdin);freopen("interval.out","w",stdout); #endifint n,m,i;scanf("%d%d",&n,&m);for(i=1;i<=n;i++){scanf("%d%d",&a[i].l,&a[i].r);a[i].id=i;}sort(a+1,a+1+n,cmp);for(i=1;i<=n;i++){if(wait.empty()==0&&wait.top().first<a[i].l){col[wait.top().second]=col[a[i].id]=m;m--;used.push(make_pair(a[i].r,a[i].id));wait.pop();if(m==0){break;}}else{if(used.empty()==0&&used.top().first<a[i].r){wait.push(used.top());col[a[i].id]=col[used.top().second];col[used.top().second]=0;used.pop();used.push(make_pair(a[i].r,a[i].id));}else{wait.push(make_pair(a[i].r,a[i].id));}}}for(i=1;i<=n;i++){if(col[i]==0&&m>=1){col[i]=m;m--;}printf("%d ",col[i]);}return 0; }
\(T2\) HZTG5809. apers \(0pts\)
-
原题: QOJ 1359. Setting Maps
-
等学了网络流再来写。
\(T3\) HZTG5810. circles \(0pts\)
-
原题: QOJ 838. Horrible Cycles
-
先按照 \(\{ a \}\) 升序排序。
-
观察到最终的环一定是左右部点交错出现。在枚举右部点的过程中,每加入一个左部点与此同时与先前的所有右部点进行连边,可以看做左部点将若干个链连接形成了环。
-
设 \(f_{i,j}\) 表示处理到第 \(i\) 个右部点且还需要 \(j\) 个左部点进行连接的环的数量。当新加入一个右部点的时候,可以作为一条单独的链加入先前状态集合,有 \(f_{i,j} \gets f_{i-1,j}+(j-1)f_{i-1,j-1}\) ,边界为 \(f_{i,1} \gets f_{i-1,1}+1\) 。接着添加左端点,类似地,有 \(f_{i,j} \gets f_{i,j}+(j+1)f_{i,j+1}\) 。
-
在更新过程中每个环仅会在最大左部点断开位置处进行计算,但每个环在两个方向上均会对答案产生一次贡献;且每条边产生了一次贡献但不应在最终的答案中,故 \(\frac{1}{2}(f_{0}-\sum\limits_{i=1}^{n}a_{i})\) 即为所求。
点击查看代码
const ll p=1000000007; ll a[5010],f[5010]; ll qpow(ll a,ll b,ll p) {ll ans=1;while(b){if(b&1){ans=ans*a%p;}b>>=1;a=a*a%p;}return ans; } int main() { #define Isaac #ifdef Isaacfreopen("circles.in","r",stdin);freopen("circles.out","w",stdout); #endifll n,sum=0,i,j,k;cin>>n;for(i=1;i<=n;i++){cin>>a[i];sum=(sum+a[i])%p;}sort(a+1,a+1+n);for(i=j=1;i<=n;i++){for(k=n;k>=2;k--){f[k]=(f[k]+(k-1)*f[k-1]%p)%p;}f[1]=(f[1]+1)%p;for(;j<=n&&a[j]==i;j++){for(k=0;k<=n;k++){f[k]=(f[k]+(k+1)*f[k+1]%p)%p;}}}cout<<(f[0]-sum+p)%p*qpow(2,p-2,p)%p<<endl;return 0; }
总结
- 整场都在罚坐。