2017.05.21练习赛赛后总结

来源:互联网 发布:水资源优化配置 编辑:程序博客网 时间:2024/06/03 15:28

A

  • 直接把块削平,排序找中位数即可
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int M=300005;ll A[M*2],n,mi;ll s[M*2],sum;int main(){    mi=1e12*M;    scanf("%d",&n);    int mid=n/2+1;    for(int i=1;i<=n;i++){        cin>>A[i];        A[i]-=abs(mid-i);    }    for(int i=1;i<=n;i++){        cin>>A[i+n];        A[i+n]-=abs(mid-i);    }    n*=2;sort(A+1,A+1+n);    for(int i=1;i<=n;i++)        s[i]=s[i-1]+A[i];    sum=s[n];    for(int i=1;i<=n;i++){        ll a=0,b=0;        if(i>1)a=A[i]*(i-1)-s[i-1];        if(i<n)b=sum-s[i]-(n-i)*A[i];        mi=min(mi,a+b);    }    cout<<mi;}`

B

思考和分析

  • 我们容易发现对于一个圆,如果它被刚好塞满了权值就为2,否则为1
    这样我们有一个很清晰的目标了即判断每个圈是否被填满
  • 我们可以很容易想到利用线段树来合并和收集信息,对于一个大圆我们先向树种放入他的子圆,之后查询它是否被填满即可
#include<bits/stdc++.h>using namespace std;#define M 300005struct node{    int x,r;    bool operator <(const node &A)const{        return r<A.r;    }}s[M];int P[M<<1];int tree[M<<3],m;map<int,int>mp;void up(int p){    tree[p]=tree[p<<1]&tree[p<<1|1];}void down(int p){    tree[p<<1]|=tree[p];    tree[p<<1|1]|=tree[p];}void update(int L,int R,int p=1,int l=1,int r=m){    if(L==l&&R==r){        tree[p]=1;        return;    }    int mid=l+r>>1;    if(R<=mid)update(L,R,p<<1,l,mid);    else if(L>mid)update(L,R,p<<1|1,mid+1,r);    else {        update(L,mid,p<<1,l,mid);        update(mid+1,R,p<<1|1,mid+1,r);    }    up(p);}bool query(int L,int R,int p=1,int l=1,int r=m){    down(p);    if(L==l&&R==r)return tree[p];    int mid=l+r>>1;    if(R<=mid)return query(L,R,p<<1,l,mid);    if(L>mid)return query(L,R,p<<1|1,mid+1,r);    return query(L,mid,p<<1,l,mid)&query(mid+1,R,p<<1|1,mid+1,r);}int main(){    int n,k=0;    scanf("%d",&n);    int ans=n+1;    for(int i=1;i<=n;i++){        scanf("%d %d",&s[i].x,&s[i].r);        P[++k]=s[i].x-s[i].r;        P[++k]=s[i].x+s[i].r;    }    sort(P+1,P+k+1);    m=unique(P+1,P+k+1)-P-1;    for(int i=1;i<=m;i++)mp[P[i]]=i;    sort(s+1,s+n+1);    for(int i=1;i<=n;i++){        bool cnt=query(mp[s[i].x-s[i].r]+1,mp[s[i].x+s[i].r]);        if(cnt)ans++;        update(mp[s[i].x-s[i].r]+1,mp[s[i].x+s[i].r]);    }    printf("%d\n",ans);    return 0;    //by Once666Ac}
  • 同时我们还有一个更简单的方法
#include<bits/stdc++.h>using namespace std;int n,stk[300005],t,ans;struct node{    int l,r;    bool operator<(const node &A)const{        if(l!=A.l)return l<A.l;        return r>A.r;    }}A[300005];int main(){    scanf("%d",&n);    for(int i=1,a,b;i<=n;i++){        scanf("%d%d",&a,&b);        A[i]=(node){a-b,a+b};    }    sort(A+1,A+1+n);//我们先以l在以r来排序一定保证先遍历大圆后遍历其中的小圆     for(int i=1;i<=n;i++){        while(t&&A[stk[t]].r<A[i].r)t--;//利用一个栈来保存大的圆,我们找到该圆外一层的圆         if(A[i].l==A[stk[t]].l)A[stk[t]].l=A[i].r;//如果相切直接把大圆的值改掉         stk[++t]=i;    }    for(int i=1;i<=n;i++)ans+=A[i].l==A[i].r;//大圆被填满了     printf("%d",ans+n+1);    return 0;}
  • 同时我还有一种神奇的方法
#include<bits/stdc++.h>using namespace std;const int M=3e5+5;priority_queue<int>L[2*M];struct node{int l,r;}G[M];int B[2*M],n,m,ans;void dfs(int l,int r){//找是否可以匹配一个l,r     if(!L[l].empty()){        int R=L[l].top();        L[l].pop();//脱皮         dfs(l,R);        if(R==r)ans++;        else dfs(R,r);    }}int main(){    scanf("%d",&n);    ans=n+1;    for(int i=1,a,b;i<=n;i++){        scanf("%d %d",&a,&b);        int l=a-b,r=a+b;        B[++m]=l;B[++m]=r;        G[i]=(node){l,r};    }    sort(B+1,B+1+m);    int k=unique(B+1,B+1+m)-B-1;    for(int i=1;i<=n;i++){        G[i].l=lower_bound(B+1,B+1+k,G[i].l)-B;        G[i].r=lower_bound(B+1,B+1+k,G[i].r)-B;        L[G[i].l].push(G[i].r);    }    for(int i=1;i<=n;i++){        if(!L[i].empty()){            int r=L[i].top();            L[i].pop();//脱皮             dfs(i,r);        }    }    printf("%d",ans);   }

C

分析和思考

  • 我们可以分步来解决
/*    同正常的hash一样我们可以把它分解成两个串    我们在%P的情况下     k=(a*33*...33)^b    k^b=a*B*B..*B;    k^b*inv(B)...inv(B)=a    我们可以先预处理出前n/2项值为S的串cnt[S]*/ #include<bits/stdc++.h>using namespace std;typedef long long ll;int n,k,p,P,e,cnt[(1<<25)+5];ll ans,inv=1,b=33;void pre(int S,int k){    if(k)for(int i=1;i<=26;++i)pre(((1ll*S*33)^i)&P,k-1);    else ++cnt[S];}void dp(int S,int k){    if(k)for(int i=1;i<=26;++i)dp((1ll*(S^i)*inv)&P,k-1);    else ans+=cnt[S];}int main(){    scanf("%d%d%d",&n,&k,&p);    P=1<<p,e=P-1;    while(e){        if(e&1)inv=inv*b%P;        b=b*b%P,e>>=1;    }--P;    pre(0,n/2);//先预处理出前一半     dp(k,n-n/2);//再计算出后一半    cout<<ans;}

小结

  • 有类似这样性质的问题我们可以通过分布来解决
    • 每个状态的转移没有顺序关系,没有后效性
    • 保证每一步和每一步是完全独立的,即可以得到这样的S=A+B而A与B的值间没有任何影响

一些细节和思路上失误


    • 离散之后变量名打错了
    • 没有想到利用线段树维护信息
    • 没有深入挖掘题目的信息,没有想到可以通过排序得到圆的关系而在奇思妙想通过一些玄学的东西来解题
  • 比赛的其他过程
    • 在B题90分后,自暴自弃没有好好Debug
    • 之后就一直抱着水分的心态乱写,浪费了许多时间
    • 受到他人的影响太大,太在意没有用的东西(排名)
    • 在B90分后做题的策略出现严重问题:不知道继续写还是Debug
原创粉丝点击