随笔 - 188  文章 - 0  评论 - 59  阅读 - 7707

2022NOIP A层联测12 染色 序列 树上询问 网络

[构造+结论]T1:给出一个序列,让你染色,要求如果(r-l)是质数,那么ar!=al,求最少出现的颜色种类。(n<=1e4)

考场

若干矛盾关系,二分图?不行,多种颜色合法解决不了-->可以像之前那样从前到后取mex?好了,复杂度对了,打上去!

正解

只要再模一下样例就会发现对于7:<=4,对于更大的数其实4也是合法的,4是一定有解,因为4是最小的合数,4k一组颜色的数一定不会矛盾;考虑最优解:1 3 6 8任意差是质数,所以>=8的4一定是最优了,但是<8特判一下,按照mex就是对的。

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define ll long long 
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=1e4+100;
int cor[N];
int n;
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("color.in","r",stdin);
    freopen("color.out","w",stdout);
    n=re();
    if(n==1)
    {
        chu("1\n1");
    }
    else if(n==2)
    {
        chu("2\n1 1");
    }
    else if(n==3)
    {
        chu("2\n1 1 2");
    }
    else if(n==4)
    {
        chu("2\n1 1 2 2");
    }
    else if(n==5)
    {
        chu("3\n1 1 2 2 3");
    }
    else if(n==6)
    {
        chu("3\n1 1 2 2 3 3");
    }
    else
    {
        chu("4\n");
        while(1)
        {
            n-=4;
            _f(i,1,4)chu("%d ",i);
            if(n<4)break;
        }
        _f(i,1,n)chu("%d ",i);
    }
    return 0;
}
/*
*/

[三分确定最优解(单峰函数)]T2:给出n,m,K,D,和序列A,求满足sigma(aibi)<=D,bi<=n的B序列,sigma(bi)+Kbi最大。(n<=2e5,D<=1e18,n<=5000)

考场

贪心a升序,那么b一定降序,要不枚举一下b?剪个枝!C,100的点根本跑不出来-->能发现什么呢?考虑邻项交换确定一下这D个空间怎么分?推个式子然后搞废了,行吧,贪心让D都分给K吧,不是随机数据吗?K很大吧?
-->0tps

正解

贪心有2个角度
【1】bn尽量大【2】b1尽量大
不妨固定一个
或者
从整体角度考虑,发现min就很突兀,考虑枚举bn的值,那么贪心策略就可以容易确定了:直接基准线分给所有数,然后剩下依次考虑b1/b2/b...bn,直接枚举肯定不行,发现枚举equal的单调性,是多峰的,三分一下。

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define ll long long 
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=2e5+100,M=5e6+100;
int T;int n,K,m;
ll sum,D,a[N];
inline ll check(ll bs)
{
    ll op=bs*m+bs*K;
    ll res=D-bs*sum;
    _f(i,1,m)
    {
        if(res<a[i])break;
        ll giv=min(res/a[i],n-bs);
        res-=giv*a[i];
        op+=giv;
    }
    return op;
}
int main()
{
   // freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("array.in","r",stdin);
    freopen("array.out","w",stdout);
    T=re();
    while(T--)
    {
        n=re(),m=re(),K=re(),D=re();
        sum=0;
        _f(i,1,m)a[i]=re(),sum+=a[i];
        sort(a+1,a+1+m);
        ll l=0,r=D/sum;
        ll ans=0;
        while((r-l)>10)
        {
            ll lmx=l+(r-l)*1.0/3,rmx=r-(r-l)*1.0/3;
            ll an1=check(lmx),an2=check(rmx);
            if(an1<an2)l=lmx;
            else r=rmx;
            ans=max({ans,an1,an2});
        }
        _f(i,l,r)ans=max(ans,check(i));
        chu("%lld\n",ans);
    }
    return 0;
}
/*
*/

[数据结构+树上差分维护点对贡献]T3:给出一颗树,n个点,Q个询问(x,y),求x到y路径上满足(dis(x,u)==u)的点数量。(n<=1e5,m<=1e5)

考场

2类点:
从x到lca路径上,(dep[u]+u==dep[x])的合法
从lca到y路径上(dep[u]-u=2*dep_lca-dep[x])的合法
但是,这东西线段树维护不了啊?还能每个点开一个值域上线段树?
暴力!

正解

其实就是开一桶维护就行,你把询问寄存到点上,dfs一遍把每个点符合【1】或者【2】要求的都加上,只需要维护一个桶,但是如果询问l,把dep[x]+x=dep[l]的点都加上了,但是lca上面的不算,所以需要在lca处减去;桶维护到目前点与root之间的点的值数。

点击查看代码




#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define ll long long 
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=3e5+100;
int head[N],tot,n,m,dep[N],md,fa[N],top[N],siz[N],mson[N];
int buc1[N*3],buc2[N*3],base;//记录:dep[x]+x, dep[x]-x
struct node
{
    int to,nxt;
}e[N<<1];
struct que
{
    int id,opt,ad,val;
};
vector<que>s[N];
int as[N];
inline void add_edge(int u,int v)
{
    e[++tot]=(node){v,head[u]};
    head[u]=tot;
}
inline void pre(int x,int ff)
{
    fa[x]=ff;siz[x]=1;
    for(rint i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==ff)continue;
        dep[to]=dep[x]+1;
        md=max(md,dep[to]);
        pre(to,x);
        siz[x]+=siz[to];
        if(siz[to]>siz[mson[x]])mson[x]=to;
    }
}
inline void slpf(int x,int belong)
{
    top[x]=belong;
    if(!mson[x])return;
    slpf(mson[x],belong);
    for(rint i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(top[to])continue;
        slpf(to,to);
    }
}
inline int get_lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        //chu("now x:%d y:%d\n",x,y);
        if(dep[top[x]]>dep[top[y]])
        {
            x=fa[top[x]];
        }
        else y=fa[top[y]];
    }
    if(dep[x]>dep[y])return y;
    return x;
}
inline void get_ans(int x,int ff)
{
    buc1[dep[x]+x]++;
    buc2[dep[x]-x+base]++;
    for(que t:s[x])
    {
        if(t.ad==1)//是左链的操作
        {
            as[t.id]+=t.opt*buc1[t.val];
        }
        else 
        {
            as[t.id]+=t.opt*buc2[t.val+base];
        }
    }
    for(rint i=head[x];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==ff)continue;
        get_ans(to,x);
    }
    buc1[dep[x]+x]--;
    buc2[dep[x]-x+base]--;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("query.in","r",stdin);
   freopen("query.out","w",stdout);
    n=re(),m=re();
    _f(i,1,n-1)
    {
        int u=re(),v=re();
        add_edge(u,v);
        add_edge(v,u);
    }
    pre(1,0);
    slpf(1,1);
    base=n+5;
    _f(i,1,m)
    {
        int l=re(),r=re();
        int lca=get_lca(l,r);
        s[l].push_back((que){i,1,1,dep[l]});
        s[lca].push_back((que){i,-1,1,dep[l]});
        s[r].push_back((que){i,1,2,2*dep[lca]-dep[l]});
        s[fa[lca]].push_back((que){i,-1,2,2*dep[lca]-dep[l]});
    }
    get_ans(1,0);
    _f(i,1,m)chu("%d\n",as[i]);
    return 0;
}
/*
9 3
5 4
4 3
3 7
4 1
1 6
1 8
1 9
5 2
6 7
2 3
3 2

*/

[构造+思维]T4:给出n条导线和m个平衡器,如果平衡器处于0状态,会有电从(yi-->xi)yi不再有电,1相反;问是否存在一平衡器的状态集合使得最终有>=n/2条导线有电(向下取整)。(n<=1e5,m<=1e6)

考场

怎么构造呢?当n=3的时候,发现怎么搞都会有电,那么n=4呢?玄乎,漫天瞎想,如果x导线已经没有电,那么之后操作一定不会让他再有电了,因为这样一直让x流空电,不会有损失,贪心呗,从1操作开始扫,如果都有电就任意选择一个流,否则贪心让没电留有电;好了,没招了,溜了。(任意有解吗?没想过)

正解

n=3(lef=2):躲电线,一定有解
n=4(lef=2):把另一条电线直接删除(尽量反向流)
细节:简化问题:发现如果重复的话正常决策不影响答案,所以可以直接忽略,搞那么多特判容易WA,然后跳过这种东西也不好调,直接拿出来
all:
从构造角度想,22一对想分组,如果(x,y)被t这个平衡器链接,那么它的电流就是可以x有可以y有,所以先预存这个状态,用二元组表示(x,y),x或者y可以有电流
考虑[x][y]->>[x,y]
[x,a][y]->[x,y][a]
[x,a][y,b]-->[x,y][a,b]
转化(这是构造,因为这样强制状态就可以保证一定不会在4_has_2_eletricity的基础上损失电流,所以要这么构造下去一定有解),用aim记录二元组。
考虑倒推答案,也是一样,复原就行。

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define ll long long 
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=5e5+100,M=5e6+100;
int X[M],Y[M],n,m,aim[N];
bool ele[N],ans[M];
pair<int,int>rem[M];
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("network.in","r",stdin);
    freopen("network.out","w",stdout);
    n=re(),m=re();
    _f(i,1,m)
    {
        X[i]=re(),Y[i]=re();
        int x=X[i],y=Y[i];
        int a=aim[x],b=aim[y];
        if(!aim[x]&&!aim[y])rem[i]=make_pair(0,0);
        else if(a==y&&b==x)rem[i]=make_pair(-1,-1);
        else if(a&&!b)
        {
            rem[i]=make_pair(a,0);
            aim[a]=0;
        }
        else if(!a&&b)
        {
            rem[i]=make_pair(0,b);
            aim[b]=0;
        }
        else 
        {
            rem[i]=make_pair(a,b);
            aim[a]=b,aim[b]=a;
        }
        aim[x]=y;aim[y]=x;
    }
    _f(i,1,n)if(aim[i]&&i<aim[i])ele[i]=1;//但是如果没有配对的呢
    f_(i,m,1)
    {
        int x=X[i],y=Y[i];
        int a=rem[i].first,b=rem[i].second;
        ans[i]=ele[y];
        if(a==-1&&b==-1)continue;
        if(!a&&!b)
        {
            ele[x]=ele[y]=1;
        }
        else if(a&&!b)
        {
            ele[a]=ele[y]=1;ele[x]=0;
            aim[a]=x;
        }
        else if(b&&!a)
        {
            ele[b]=ele[x]=1;ele[y]=0;
            aim[b]=y;
        }
        else 
        {
            if(ele[a]==0)ele[x]=1,ele[y]=0;
            else ele[x]=0,ele[y]=1;
            aim[a]=x;aim[b]=y;
        }
        aim[x]=a,aim[y]=b;
    }
    chu("YES\n");
    _f(i,1,m)chu("%d",ans[i]);
    return 0;
}
/*
3 3
1 2
2 3
3 1
*/

暴力60tps

直接枚举操作,每个情况都有2种转移,理论上是2^m级别的搜索,但是可以过m=1000000的点......
其实是有道理的,因为这种点它n很小,你只要保证lef<n/2的情况中断掉,真正保留下来的决策很少(<=4步之内就可以排除很多状态),加上你贪心地选留的多的,合法立即回溯,所以可以很优秀。

点击查看代码




#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=(a);i<=(b);++i)
#define f_(i,a,b) for(register int i=(a);i>=(b);--i)
#define ll long long 
#define ull unsigned long long
#define chu printf
#define rint register int
inline ll re()
{
    ll x=0,h=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')h=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*h;
}
const int N=5e5+100,M=5e6+100;
int st[M],ed[M],n,m;
bool ele[N],as[M];
inline bool dfs(int now,int lef)
{
    if(lef<(n+1)/2)return 0;
    if(now>m)return 1;
    if(ele[st[now]]&&ele[ed[now]])
    {
        as[now]=1;
        ele[st[now]]=0;
        if(dfs(now+1,lef-1))return 1;
        as[now]=0;
        ele[st[now]]=1,ele[ed[now]]=0;
        if(dfs(now+1,lef-1))return 1;
        ele[st[now]]=ele[ed[now]]=1;
    }
    else if(ele[st[now]]&&!ele[ed[now]])
    {
        as[now]=0;
        if(dfs(now+1,lef))return 1;
        as[now]=1;
        ele[st[now]]=0;ele[ed[now]]=1;
        if(dfs(now+1,lef))return 1;
        ele[st[now]]=1;ele[ed[now]]=0;
    }
    else if(ele[ed[now]]&&!ele[st[now]])
    {
        as[now]=1;
        if(dfs(now+1,lef))return 1;
        as[now]=0;
        ele[ed[now]]=0;ele[st[now]]=1;
        if(dfs(now+1,lef))return 1;
        ele[st[now]]=0;ele[ed[now]]=1;
    }
    else
    {
        as[now]=0;
        if(dfs(now+1,lef))return 1;
    }
    return 0;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    freopen("network.in","r",stdin);
    freopen("network.out","w",stdout);
    n=re(),m=re();
    _f(i,1,m)st[i]=re(),ed[i]=re();
    fill(ele+1,ele+1+n,1);
    dfs(1,n);
    chu("YES\n");
    _f(i,1,m)chu("%d",as[i]);
    return 0;
}
/*
*/

posted on   HZOI-曹蓉  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示