2021牛客暑期多校训练营3

很费劲地 3/10 ,可是校内第18名囧。ios

一开始没什么一眼作出来的题;后来发现J题好多人过,因而G开始写J;但彷佛写了一会之后遇到了障碍,停下继续推了起来。而我先看了B,E,F,发现B题渐渐有一些人过了,因而开始看B。此时Y也在看B。一个小时多一点的时候,J过了;Y在看E,我在看B,而后G看F——目前就是B,E,F,J过的人比较多。B应该是 \( O(n^2) \) 作,就开始想DP,可是没什么很好的想法。以后Y发现了E题的规律,写出了个式子,因而我两人就一块儿讨论怎么写出来;讨论一番,以为预处理一下,而后二分就能写。此时G发现F题能搜索,已写了半天。而后Y去写E题,很快写完,但遇到了点问题,G也去调了半天,就过了。这时已经四点了。G继续写F题,而Y和我一块儿看B题;我又写了DP的转移方程,可是怎么看也是 \( O(n^3) \) 的,苦恼。Y在考虑构造,但也没想好。还剩十分钟的时候F题终于过了。因而我上去写了个B题的简单假算法,果真WA了,并且还一个点都没过,真不给面子啊。c++

E:Math算法

题意:数组

\(t\)组数据,每次给一个\(n\),求知足\( (xy+1) | (x^2+y^2) \)的\( (x,y) \)个数,\(1 \leq x,y \leq n\)。\(1 \leq t \leq 10^5, 1 \leq n \leq 10^18\)。ide

分析:spa

经过打表,发现除了\((1,1)\),知足条件的是全部\( (k,k^3) \),以及它们递推下去的数对;code

就是说,若是\((a,b)\)知足条件,并且是从\((k,k^3)\)推出来的,那么\( (b,b*k^2-a) \)也是知足条件的数对;ci

因此找到全部知足条件的数对(也不是不少),每次询问upper_bound便可。get

数字比较大,须要用__int128.博客

Y写了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e7+10;
ll num[MAXN];
int T,cnt;
ll ans;
void init(){
    num[1]=1;cnt=1;
    __int128 a,b;
    for(int i=2;i<=1e6;i++){
        a=i;b=i,b=b*i*i;
        while(b<=1e18){
            //if(b==2211||b==2424||b==4863) printf("%lld %lld\n",(ll)a,(ll)b);
            num[++cnt]=(ll)b;
            ll t=b;
            b=b*i*i-a;a=t;
            //printf("i=%d a=%lld b=%lld\n",i,(ll)a,(ll)b);
            //if(b>1e18||b<0) printf("i=%d b=%lld\n",i,(ll)b),exit(0);
        }
    }
    sort(num+1,num+1+cnt);
    cnt=unique(num+1,num+1+cnt)-num-1;
}
int main()
{   //freopen("E.in","w",stdout);
    init();
    //printf("cnt=%d\n",cnt);
    //for(int i=1;i<=40;i++) printf("%d %lld\n",i,(ll)num[i]);
    for(scanf("%d",&T);T;T--){
        ll n;
        scanf("%lld",&n);
        ans=upper_bound(num+1,num+1+cnt,n)-num-1;
        printf("%lld\n",ans);
    }
}
Y

 

G:Yu Ling(Ling YueZheng) and Colorful Tree

题意:

给定一棵\( n \)个点的树(有边权),给\( q \)次操做,分为两种:

\( 0 , u , x \) :把点\( u \)染成颜色\( x \),这里颜色彼此不一样并且范围在\( n \)之内;

\( 1 , u , l , r , x \) :查询离\( u \)最近的、染过色并且颜色\( c \)知足\( l \leq c \leq r , c mod x = 0 \)的祖先到\( u \)的距离。

\( 1 \leq n , q \leq 1.1*10^5 \) 。

分析:

看了第一个经过的队伍的代码,居然是树状数组套线段树,复杂度\( O(nlog^2(n)) \),很巧妙。

首先,须要处理\( c \% x = 0 \)这个要求——能够离线作,而后把操做分类,将有倍数关系的操做创建关系;暂时能够这样想。

查询要找的是祖先——能够反过来想,染色操做会对被染色点的子树有影响,而操做子树貌似比操做祖先方便一些。

综合起来,咱们能够先找到每一个颜色的因数,把它们存在vector里;染上某个颜色的操做会影响到该颜色全部的因数,因而把这个操做分别放进它全部因数的存储操做的另外一个vector里;固然,查询某个颜色的操做也放进那个颜色的操做vector里;这个vector里的操做要按顺序放。

因而咱们能够考虑每一个颜色的操做序列;按顺序进行修改和查询操做。

为了方便地对子树总体修改,咱们求出这棵树的dfs序,在dfs序列上用树状数组进行区间操做,给染色点的子树进行某些修改。

下面咱们考虑\( l \leq c \leq r \)这个要求——若是用线段树维护颜色值序列,那么这个也能够方便地查询;

也就是说,当出现一个查询操做时,咱们要在查询点的线段树内查找\( l , r \)范围内是否有数——只要有数便可,由于前面只进行过当前颜色的倍数颜色的染色操做。

如今咱们就能够写出树状数组套线段树了:树状数组建在dfs序列上,其中每一个点有一个线段树建在颜色数值上;修改操做就是给修改点的子树中每一个点的线段树上染色颜色位置\( +1 \),查询操做就是在查询点的线段树上提取\( l , r \)区间的和;

这时查询操做作了一半——咱们能够知道对于当前点,有多少个祖先符合条件;

倍增记录的祖先节点,咱们能够找一下到哪一个祖先节点时,它也有一样个数的祖先符合条件;最浅的那个点就是答案,\( dis[u] \)相减即得距离。

那么空间复杂度怎么办?实际上,咱们能够用到线段树节点时再把它开出来;而对于每一个颜色的操做咱们最多用到\( qlog^2(n) \)个线段树点,因此用时再开,用完删除便可。

时间复杂度是\( O(nlog^2(n)) \)。

(过程当中顺便仔细又看了看树状数组,这篇博客挺好的。)

代码以下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<iostream>
#include<vector>
#define mid ((l+r)>>1)
#define pb push_back
#define ll long long
using namespace std;
int const N=110005,M=20000005;///
int n,q,hd[N],nxt[N<<1],cnt,to[N<<1],fa[N][20],dfn[N],ed[N],dcnt;
int rt[N],ls[M],rs[M],tcnt,tr[M];
ll w[N<<1],dis[N],ans[N];
vector<int>ele[N];
struct Nd{
    int u,l,r,id;
};
vector<Nd>v[N];
void init()
{
    for(int x=1;x<=n;x++)
        for(int j=x;j<=n;j+=x)ele[j].pb(x);//j mod x = 0 同余类
}
void add(int x,int y,ll s){nxt[++cnt]=hd[x]; hd[x]=cnt; to[cnt]=y; w[cnt]=s;}
void dfs(int u)
{
    dfn[u]=++dcnt;
    for(int i=hd[u],v;i;i=nxt[i])
    {
        if((v=to[i])==fa[u][0])continue;
        fa[v][0]=u; dis[v]=dis[u]+w[i];
        //dfs(v);//
        for(int i=1;i<20;i++)
            fa[v][i]=fa[fa[v][i-1]][i-1];
        dfs(v);//注意顺序TAT
    }
    ed[u]=dcnt;
}
//线段树,序列是染色数值
void chg2(int &x,int l,int r,int p,int s)
{
    if(!x)x=++tcnt; tr[x]+=s;
    if(l==r)return;
    if(p<=mid)chg2(ls[x],l,mid,p,s);
    else chg2(rs[x],mid+1,r,p,s);
}
int qry2(int x,int l,int r,int ql,int qr)
{
    if(!x||(l>=ql&&r<=qr))return tr[x];
    int ret=0;
    if(mid>=ql)ret+=qry2(ls[x],l,mid,ql,qr);
    if(mid<qr)ret+=qry2(rs[x],mid+1,r,ql,qr);
    return ret;
}
//树状数组,序列是dfs序
void chg1(int x,int p,int s){for(;x<=n;x+=(x&(-x)))chg2(rt[x],1,n,p,s);}
int qry1(int x,int l,int r){int ret=0; for(;x;x-=(x&-x))ret+=qry2(rt[x],1,n,l,r); return ret;}
void dell(int x){for(;x<=n;x+=(x&-x))rt[x]=0;}
void del(int x)
{
    while(tcnt)ls[tcnt]=0,rs[tcnt]=0,tr[tcnt]=0,tcnt--;
    //for(int i=1;i<=n;i++)rt[i]=0;
    for(Nd it:v[x])
        if(it.id==0)dell(dfn[it.u]),dell(ed[it.u]+1);
}
void work(int x)//树状数组单点修改(差分),区间查询(前缀和) //此处x是颜色
{
    //printf("x=%d\n",x);
    for(Nd it:v[x])
    {
        if(it.id==0)//若是是修改,则子树内各点的线段树上所染颜色位置+1
        {
            //printf("stchg\n");
            chg1(dfn[it.u],it.l,1);
            chg1(ed[it.u]+1,it.l,-1);
            //printf("aftchg\n");
        }
        else//若是是查询
        {
            //printf("stq\n");
            int u=it.u,L=it.l,R=it.r,id=it.id;
            //printf("u=%d L=%d R=%d id=%d\n",u,L,R,id);
            int num=qry1(dfn[u],L,R),p=u;
            if(!num){ans[id]=-1; continue;}
            for(int i=19;i>=0;i--)
                if(qry1(dfn[fa[p][i]],L,R)==num)p=fa[p][i];
            //printf("u=%d p=%d %lld %lld\n",u,p,dis[u],dis[p]);
            ans[id]=dis[u]-dis[p];
            //printf("aftq\n");
        }
    }
    del(x);//删除线段树
}
int main()
{
    scanf("%d%d",&n,&q); init();
    for(int i=1,u,v,s;i<n;i++)
        scanf("%d%d%d",&u,&v,&s),add(u,v,s),add(v,u,s);
    dfs(1);
    //v[x]:x的0同余类下的修改与查询操做,并且是按顺序放的
    for(int i=1,tp,u,a,b,c;i<=q;i++)
    {
        scanf("%d%d%d",&tp,&u,&a);
        if(tp==0)
        {
            for(int it:ele[a])v[it].pb((Nd){u,a,0,0});//it做为询问中的x时可查的点增长了
            ans[i]=-2;
        }
        else
        {
            scanf("%d%d",&b,&c);
            if(((b+1)/c)==((a-1)/c))ans[i]=-1;//(r+1)/x==(l-1)/x,即不存在l--r中的数mod x = 0
            else v[c].pb((Nd){u,a,b,i});
        }
    }
    for(int x=1;x<=n;x++)work(x);//此处x是颜色
    for(int i=1;i<=q;i++)
        if(ans[i]!=-2)
            if(ans[i]==-1)printf("Impossible!\n");
            else printf("%lld\n",ans[i]);
    return 0;
}
me

 

J:Counting Triangles

题意:

给出\(n\)个点的彻底图,每条边是黑色或白色;求同色三角形的个数。\(1 \leq n \leq 8000\)。

分析:

从\(1\)到\(n\),记录每一个点左边、右边的白边点和黑边点各有多少个,算出不一样色三角形的个数;每一个三角形被算了两边(三个点中有两个点连了异色边),最后除以二,再从全部三角形中减去便可。

G写了。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk= watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=
#include<bits/stdc++.h>
#define ll long long
using namespace std;
namespace GenHelper
{
    unsigned z1,z2,z3,z4,b,u;
    unsigned get()
    {
        b=((z1<<6)^z1)>>13;
        z1=((z1&4294967294U)<<18)^b;
        b=((z2<<2)^z2)>>27;
        z2=((z2&4294967288U)<<2)^b;
        b=((z3<<13)^z3)>>21;
        z3=((z3&4294967280U)<<7)^b;
        b=((z4<<3)^z4)>>12;
        z4=((z4&4294967168U)<<13)^b;
        return (z1^z2^z3^z4);
    }
    bool read() {
      while (!u) u = get();
      bool res = u & 1;
      u >>= 1; return res;
    }
    void srand(int x)
    {
        z1=x;
        z2=(~x)^0x233333333U;
        z3=x^0x1234598766U;
        z4=(~x)+51;
          u = 0;
    }
}
using namespace GenHelper;
int n;
ll Tot,Ill;
bool Edge[8005][8005];
int Input()
{   int n, seed;
    cin >> n >> seed;
    srand(seed);
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++)
            Edge[j][i] = Edge[i][j] = read();
    return n;
}
int main()
{   n=Input(),Tot=1ll*n*(n-1)*(n-2)/6;
    for(int i=0;i<n;i++)
    {   ll Rb=0,Rw=0,Lb=0,Lw=0;
        for(int j=i-1;j>=0;j--) Edge[i][j]?Lb++:Lw++;
        for(int j=i+1;j<n;j++) Edge[i][j]?Rb++:Rw++;
        Ill+=Lb*Rw+Lw*Rb+Lw*Lb+Rw*Rb;
    }
    printf("%lld\n",Tot-Ill/2);
}
G

 

https://ac.nowcoder.com/acm/contest/11254/E

相关文章
相关标签/搜索