ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

6.22 NOI 模拟

2022-06-23 09:36:00  阅读:117  来源: 互联网

标签:NOI int mid long define 6.22 now 模拟 dis


\(T1\)递归

给出 \(Thue-Morse\) 序列的定义三

每次 \(0\rightarrow 01\),\(1\rightarrow 10\)

\(0\rightarrow 01 \rightarrow 0110 \rightarrow 01101001\rightarrow...\)

我们现在已知串 \(010010\) 考虑将其划分

\(0\ 10\ 01\ 0\) 或者 \(01\ 00 \ 10\)

显然第二个是不合法的。

我们把第一个补全,为 \(10\ 10\ 01\ 01\)

然后合并一下为 \(1100\) ,我们假设 \(1100\) 出现的位置是 \(i\) 我们 \(010010\) 出现的位置为 \(2i+1\)

然后我们得到递推式 \(f(l,r)=f(\lfloor l/2\rfloor,\lfloor r/2\rfloor)+(l\mod 2)\)

我们只需要暴力求小数据即可

#include<bits/stdc++.h>
using namespace std;
int f[3][8]={{0,1},{5,2,0,1},{0,4,3,1,5,2}};
long long slo(long long l,long long r)
{
    if(r-l+1>=4) return 2*slo(l/2,r/2)+(l&1);
    long long S=0;
    for(long long i=l;i<=r;i++)
    {
        S|=(__builtin_popcountll(i)&1)<<(i-l);
    }
    return f[r-l][S];
}
int main()
{
    int q;
    scanf("%d",&q);
    while(q--)
    {
        long long l,r;
        scanf("%lld%lld",&l,&r);
        cout<<slo(l,r)<<"\n";
    }
}

$T2\ $加边

按照原图跑以 \(1\) 为根的 \(bfs\) 树

\(b>2\times a\)答案是 \(dep\times a\)

否则对于 \(b\) 边进行 \(bfs\) ,类似三元环进行删边,复杂度可以保证在 \(O(m\sqrt m)\)

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define MAXN 200005
using namespace std;
#define FastIO
#ifdef FastIO
    char buf[1<<21],*p1,*p2;
    #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
#endif
template<class T>
T Read()
{
    T x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}
int (*read)()=Read<int>;
#define read Read<int>
vector<int>rd[MAXN],gd[MAXN];
int dis[MAXN],dep[MAXN],n,m,a,b;
void add(int u,int v)
{
    rd[u].push_back(v);
    gd[u].push_back(v);
}
void bfs()
{
    queue<int>q;
    memset(dep,-1,sizeof(dep));
    q.push(1);
    dep[1]=0;
    while(q.size())
    {
        int now=q.front();
        q.pop();
        for(int i=0;i<rd[now].size();i++)
        {
            int y=rd[now][i];
            if(dep[y]!=-1) continue;
            q.push(y); dep[y]=dep[now]+1;
        }
    }
}
bitset<MAXN>Min;
void bfs_dis()
{
    queue<int>q;
    memset(dis,-1,sizeof(dis));
    q.push(1);
    dis[1]=0;
    while(q.size())
    {
        int now=q.front();
        q.pop();
//      Min.reset();
        for(vector<int>::iterator it=rd[now].begin();it!=rd[now].end();)
        {
            int y=*it; 
            Min[y]=1;
            it++;
        }
        for(vector<int>::iterator it=rd[now].begin();it!=rd[now].end();)
        {
            int y=*it;
            if(dis[y]!=-1) it=rd[now].erase(it);
            else it++;
            for(vector<int>::iterator it1=gd[y].begin();it1!=gd[y].end();)
            {
                int ty=*it1; 
                if(dis[ty]!=-1)
                {
                	it1=gd[y].erase(it1);
				}
				else
			    {
			    	it1++;
					if(Min[ty]) continue;
	                dis[ty]=dis[now]+b;
	                q.push(ty);
				}
            }
        }
        for(int i=0;i<rd[now].size();i++)
        {
            int y=rd[now][i];
            Min[y]=0;
        }
    }
}
signed main()
{
    scanf("%lld%lld%lld%lld",&n,&m,&a,&b);
    for(int i=1,u,v;i<=m;i++)
    {
        u=read();v=read();
        add(u,v); add(v,u);
    }
    bfs();
    if(b>=2*a)
    {
       for(int i=2;i<=n;i++)
       {
           cout<<dep[i]*a<<"\n";
       }
    }
    else
    {
        bfs_dis();
        for(int i=2;i<=n;i++)
        {
            if(dep[i]%2==0)
            {
               cout<<(dep[i]/2)*b<<"\n";
            }
            else
            {
               if(dis[i]==-1) cout<<dep[i]/2*b+a<<"\n";
               else cout<<min(dep[i]/2*b+a,dis[i])<<"\n";
            }
        }
    }
}

$T3\ $虐场

考虑枚举 \(k\),考虑已知 \(k\) 之后应该怎么求解

设 \(c_i=b_{j+1}-b_{j}-k\)表示空场的和

我们先选定连续 \(n\) 场,向左右移动,可以导致 \(b\) 整体加减 \(1\),\(c\) 不变

首先最大化收益,先考虑选最右侧 \(n\times k\) 场,然后往左移动

设 \(d_i=b_i-a_i\) 我们要 $d_i\leq 0 $,并且 \(\sum |b_i|\) 尽可能小

每次贪心的话,就把目前后缀最大值位置向左平移,最后每个位置移动的位置是后缀的最大值

设后缀最大值 \(suf_i\),答案是 \(\sum (suf_i-b_i)\)

至于移动限制考虑我们只能进行 \(m-k\times n\) 次前缀减,后面的只能进行整体减就好了

发现答案是关于 \(k\) 的凸函数,可以三分找极值点

#define Eternal_Battle ZXK
#include<bits/stdc++.h>
#define int long long
#define MAXN 200010
using namespace std;
int n,m,k,lim,a[MAXN],b[MAXN];
int check(int x)
{
    long long nw=0;
    for(int i=1;i<=n;i++)
    {
        b[i]=m*x-x*(x+1)/2*n+i*x-a[i];
        nw=max(nw,b[i]);
    }
    long long an=0,sum=max(nw-m+x*n,0ll);
    for(int i=n;i>=1;i--)
    {
        sum=max(sum,b[i]);
        an+=sum-b[i];
    }
    return an;
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    k=lim=m/n;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        int l=1,r=m/n;
        while(l<=r)
        {
            long long mid=(l+r)>>1;
            if(mid*(mid-1)/2*n+i*mid<=a[i]) l=mid+1;
            else r=mid-1;
        }
        lim=min(lim,r);
        l=1,r=lim;
        while(l<=r)
        {
            long long mid=(l+r)>>1;
            if(m*mid-mid*(mid+1)/2*n+i*mid-a[i]-m+mid*n<=0) l=mid+1;
            else r=mid-1;
        }
        k=min(l,k);
    }
    int ans=check(min(lim,k));
    int l=0,r=k-1;
    while(r-l>10)
    {
        int mid=(l+r)>>1;
        long long an1=check(mid),an2=check(mid+1);
        if(an1<an2) r=mid-1;
        else l=mid+2;
    }
    for(int i=l;i<=r;i++)
    {
        ans=min(ans,check(i));
    }
    ans=-ans;
    for(int i=1;i<=n;i++)
    {
        ans+=a[i];
    }
    cout<<ans<<endl;
}

标签:NOI,int,mid,long,define,6.22,now,模拟,dis
来源: https://www.cnblogs.com/Eternal-Battle/p/EB_is_cute.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有