Educational Codeforces Round 122 (Rated for Div. 2)思路分享

Educational Codeforces Round 122 (Rated for Div. 2)

明明会的题,却因为手抖,太慌张,多了很多的罚时,要不然又能大涨一波了....

A. Div. 7

修改一个数的某一位,使得它变成7的倍数。首先的想法就是减去余数,但这样的话,可能个位上的数字不足,这样的话会导致借位的情况,所以还有一个情况就是加上7-余数,补足这个余数。考虑第一种情况会出现特例的情况,个位为x(0-9),余数为d(0-6),x<d,则x+7-d,可以发现,d最大是6,x最小是0,这样的话结果为1,还有由于x<d的缘故,x-d一定是负数,所以x+7-d<7,一定符合条件。所有直接考虑这两种情况即可。

B. Minority

B题要求选定一个区间,使得这个区间内0/1的个数严格小的个数足够大。
直接说结论:若整个序列的0/1个数不相等的话,答案为整个区间。这个很显然,因为任何一个区间的严格小的个数都不可能大于这个区间严格小的个数。若相等的话,则答案为个数-1.这个也很好证,就是我们将第一个字符去掉即可。

C. Kill the Monster

直接枚举即可。

D. Make Them Equal

比较显然的DP吧。我们可以预处理出使每一位有价值的最少的操作次数。之后直接做背包就行。

E. Spanning Tree Queries

这个题也很有趣。
先思考暴力的话,每次询问我们都重排一次,然后做一遍kruskal。但是你看看这个询问的次数1e7,强制O(n)的算法才行.....
题解大法好啊.其实我做题的时候也有这个思路(马后炮ing)。我们可以先按照原始的边权排序。考虑在有询问x的情况下,两个边权分别为u,v的边如何排序。显然在负无穷到u+v/2的时候,u排在前面。之后v排在后面,我们称这是一次交换操作。考虑全部的边(排过序之后),在x从负无穷到正无穷的情况下,总共会有多少种排列。由于任意两条边最多只有一次交换,所以一共最多有m2个交换,那么相对应最多有m2中排列方式。所以无论x为何值,我们总的排列的方式并不多。知道这点后,我们完全可以先将所有的询问排序,然后将所有的特殊点(即会影响某两个边交换的边权值)排序,依次去做最小生成树。保存生成树中的边权,之后直接二分计算答案即可。细节挺多....

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=52,M=310,maxn=1e7+10;
int n,m,qv,p,k,A,B,C,q[maxn],f[N];
ll ans,sum[N];
vector<int>v;
vector<int>s;
struct bian{int x,y,v;}a[M];
inline bool cmp(bian x,bian y){return  abs(x.v-qv)<abs(y.v-qv);} 
inline int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
inline void solve()
{
    s.clear();
    for(int i=1;i<=n;++i) f[i]=i;
    sort(a+1,a+m+1,cmp);
    int cnt=0;
    for(int i=1;i<=m;++i)
    {
        int t1=getf(a[i].x),t2=getf(a[i].y);
        if(t1!=t2)
        {
            f[t1]=t2;
            s.push_back(a[i].v);
            if(++cnt==n-1) break;
        }
    }
    sort(s.begin(),s.end());
    for(int i=0;i<s.size();++i) 
    {
        sum[i]=s[i];
        if(i) sum[i]+=sum[i-1];   
    }
}
int main()
{
//    freopen("1.in","r",stdin);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    scanf("%d%d%d%d%d",&p,&k,&A,&B,&C);
    for(int i=1;i<=p;++i) scanf("%d",&q[i]);
    for(int i=p+1;i<=k;++i) q[i]=((ll)q[i-1]*A+B)%C;
    sort(q+1,q+k+1);
    for(int i=1;i<=m;++i)
        for(int j=i+1;j<=m;++j) 
        {
            if(a[i].v==a[j].v) continue;
            v.push_back((a[i].v+a[j].v)/2+1);
        }
    sort(v.begin(),v.end());
    int num=unique(v.begin(),v.end())-v.begin();
    solve();
    int l=1;
    for(int i=0;i<num;++i)
    {
        int x=v[i];
        while(l<=k&&q[l]<x)
        {
            int id=lower_bound(s.begin(),s.end(),q[l])-s.begin();
            if(id==n-1||id==0) ans^=llabs((ll)(n-1)*q[l]-sum[n-2]);
            else
            {
                ll sur=(sum[n-2]-sum[id-1]-(ll)(n-1-id)*q[l]);
                ll sut=(ll)q[l]*id-sum[id-1];
                ans^=sur+sut;
            }
            ++l;
        }
        qv=x;
        solve();
    }
    while(l<=k)
    {
        int id=lower_bound(s.begin(),s.end(),q[l])-s.begin();
        if(id==n-1||id==0) ans^=llabs((ll)(n-1)*q[l]-sum[n-2]);
        else
        {
            ll sur=(sum[n-2]-sum[id-1]-(ll)(n-1-id)*q[l]);
            ll sut=(ll)q[l]*id-sum[id-1];
            ans^=(sur+sut);
        }
        ++l;
    }
    printf("%lld",ans);
    return 0;
} 
posted @ 2022-02-06 22:10  逆天峰  阅读(50)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//