高一上七月中旬日记

7.11

闲话

  • 起床后被小孩哥临时班主任问在哪里跑操,我说我们不跑操。
  • 吃早饭时遇到了 FY 办的英语夏令营的人,但为啥跟我们在一个食堂吃饭,还比我们吃饭早。
  • 上午 \(7:30 \sim 11:30\) \(miaomiao\) 安排了一场模拟赛。
  • 午休 \(field\) 查宿,颓《平凡的世界》。
  • 因上午模拟赛其他学校前天已经打了,今天下午只组织了线下讲题。
  • 晚上 \(miaomiao\) 让查中考志愿的录取情况。看了眼是 HS 普惠,貌似上 HS 的只有 @jijidawang@wkh2008@hs_mo 和我。
  • 晚上 \(miaomiao\) 说晚上可以写写博客总结下模拟赛的题;让多注意部分分的写法,因为要是稍微题简单点,南方强省就一车 \(AK\) 的,咱就没法比了;解释了下我们为啥没和高二待在一起的理由,机房太小,虽说能坐满但有点挤,空调制冷功率太小;不要做无用功;称“用刀切西瓜”为“拿刀把这西瓜宰了”。

做题纪要

GHzoj 3765. 超市抢购

luogu P3515 [POI2011] Lightning Conductor

GHzoj 3768. 龙珠游戏

GHzoj 3766. 核酸检测

GHzoj 3767. 七龙珠

7.12

闲话

  • 上午 \(7:30 \sim 11:30\) \(miaomiao\) 安排了一场模拟赛,还把网关了。
  • 打模拟赛时挠了挠头,把眼镜从头上挠掉了,左眼镜腿彻底断了。
  • 回宿舍后拿上铺学长留下来的捆蚊帐的绳子剪下来一段和眼镜腿绑在一起。午休现班主任和数奥交流查宿。睡觉后小孩哥在楼道进行火力覆盖,影响我颓《平凡的世界》了。
  • 下午和高二的一起讲题。
  • @STA_Morlin 因不好好打模拟赛,不写暴力代码被 \(huge\) \(D\) 了,顺便说让我们练习打暴力分。
  • 临近吃晚饭的时候 \(miaomiao\) 把西瓜宰了, \(505\) \(13\) 个人分一个西瓜, \(504\) \(22\) 人左右分一个西瓜。吃了三块瓜给吃撑了。
  • 晚上临下课时 \(huge\) 说明天的模拟赛是 HZOI2019 @liuchanglc 出给多校的,顺便让我们先帮他验验题,让我们明天吃早饭前把电脑重启一遍,给隔壁高二的给点刺激,“展示下我们强大的实力”,还有正好打完模拟赛身子就热了,出去吃个饭放假刚刚好,好奇 \(huge\) 的逻辑;强调了下暑假集训期间内务、(宿舍)纪律、卫生但凡出问题,就停课半天整改,加负责一暑假 \(504\) 的卫生,还 \(D\) 了下在场的 @int_R ,在其他班主任看来还有点轻,但 \(huge\) 说这是他能下定决心做的最大处罚,以前几届管得没那么严,但现在想好好管管了,可能是因为 \(bobo\) 走了?

做题纪要

CF1715E Long Way Home

  • 分层图上进行斜率优化 \(DP\)

  • \(f_{i}\) 表示从起点飞到点 \(i\) 的最小距离,状态转移方程为 \(f_{i}=\min\limits_{j=1}^{n} \{ f_{j}+(i-j)^{2} \}\)

  • 去掉 \(\min\) ,有 \(\begin{cases} x=j \\ y=f_{j}+j^{2} \\ k=2i \\ b=f_{i}-i^{2} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 是单调递增的。

  • 每次进行状态转移后将更改后的值重新跑最短路。

  • 移项相乘会炸 long long ,貌似 \(CF\) __int128_t\(CE\)

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[200010];
    ll head[200010],dis[200010],vis[200010],f[200010],cnt=0;
    priority_queue<pair<ll,ll> >dijkstra_q;
    deque<ll>q;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra()
    {
    	ll x,i;
    	memset(vis,0,sizeof(vis));
    	while(dijkstra_q.empty()==0)
    	{
    		x=-dijkstra_q.top().second;
    		dijkstra_q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					dijkstra_q.push(make_pair(-dis[e[i].to],-e[i].to));
    				}
    			}
    		}
    	}
    }
    ll x(ll j)
    {
    	return j;
    }
    ll y(ll j)
    {
    	return f[j]+j*j;
    }
    double ask_k(ll x1,ll y1,ll x2,ll y2)
    {
    	return (x1==x2)?0x7f7f7f7f:1.0*(y2-y1)/(x2-x1);
    }
    int main()
    {
    	ll n,m,k,u,v,w,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		add(u,v,w);
    		add(v,u,w);
    	}
    	memset(dis,0x3f,sizeof(dis));
    	dis[1]=0;
    	dijkstra_q.push(make_pair(0,-1));
    	dijkstra();
    	for(j=1;j<=k;j++)
    	{
    		q.clear();
    		q.push_back(0);
    		for(i=1;i<=n;i++)
    		{
    			f[i]=dis[i];
    			while(q.size()>=2&&ask_k(x(q.back()),y(q.back()),x(q[q.size()-2]),y(q[q.size()-2]))>=ask_k(x(i),y(i),x(q.back()),y(q.back())))
    			{
    				q.pop_back();
    			}
    			q.push_back(i);
    		}
    		for(i=1;i<=n;i++)
    		{
    			while(q.size()>=2&&ask_k(x(q[1]),y(q[1]),x(q.front()),y(q.front()))<=2.0*i)
    			{
    				q.pop_front();
    			}
    			if(f[q.front()]+(i-q.front())*(i-q.front())<dis[i])
    			{
    				dis[i]=f[q.front()]+(i-q.front())*(i-q.front());
    				dijkstra_q.push(make_pair(-dis[i],-i));
    			}
    		}
    		dijkstra(); 
    	}
    	for(i=1;i<=n;i++)
    	{
    		cout<<dis[i]<<" ";
    	}
    	return 0;
    }
    

CF1114F Please, another Queries on Array?

luogu P2966 [USACO09DEC] Cow Toll Paths G

luogu P3474 [POI2008] KUP-Plot purchase

luogu P3591 [POI2015] ODW

7.13

闲话

  • 起床时发现外面在下雨,好一个雨假同期论。
  • 上午 \(7:30 \sim 11:30\) \(miaomiao\) 安排了一场模拟赛,题解先暂时不写了。
  • \(11:20\) \(miaomiao\) 就说可以回宿舍收拾行李了,将电脑注销后就立刻走了,走的时候没听清 \(miaomiao\) 给屋里人说的啥,好像是走的时候别给电脑设密码,要不然他打不开啥的,感觉就是在说我。
  • 然后就是放假。

做题纪要

HDU7520 cats 的重力拼图

  • 若在外围则只能走外围,在内部的话先走到外围,特判下边界。

    点击查看代码
    int main()
    {
        ll t,n,m,a,b,i;
        cin>>t;
        for(i=1;i<=t;i++)
        {
            cin>>n>>m>>a>>b;
            if(1<a&&a<n&&1<b&&b<m)
            {
                cout<<2*n+2*m-4+max(n,m)-2<<endl;
            }
            else 
            {
                if(n==1)
                {
                    cout<<m<<endl;
                }
                else
                {
                    if(m==1)
                    {
                        cout<<n<<endl;
                    }
                    else
                    {
                        if((a==1||a==n)&&(b==1||b==m))
                        {
                            cout<<2*n+2*m-4<<endl;
                        }
                        else
                        {
                            if(b==1||b==m)
                            {
                                cout<<2*n+2*m-4+m-2<<endl;
                            }
                            else
                            {
                                cout<<2*n+2*m-4+n-2<<endl;
                            }
                        }
                    }
                }
            }
        }
        return 0;
    }
    

luogu P1775 石子合并(弱化版)

luogu P3919 【模板】可持久化线段树 1(可持久化数组)

  • 可持久化线段树板子。

    • 线段树上修改至多会影响 \(O( \log n)\) 个顶点,新建这些顶点,其它节点链接到上一个版本,延用上一个版本的信息。
    • 时空复杂度为 \(O(m \log n)\)
    点击查看代码
    int a[1000010];
    struct PDS_SMT
    {
    	int root[1000010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[1000010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)//注意 rt 的引用
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			tree[rt].sum=a[l];
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos,int val)//注意 rt 的引用
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		if(l==r)
    		{
    			tree[rt].sum=val;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos,val);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos,val);
    		}
    	}
    	int query(int rt,int l,int r,int pos)
    	{
    		if(l==r)
    		{
    			return tree[rt].sum;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			return query(lson(rt),l,mid,pos);
    		}
    		else
    		{
    			return query(rson(rt),mid+1,r,pos);
    		}
    	}
    }T;
    int main()
    {
    	int n,m,v,pd,pos,val,i;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    	}
    	T.build_tree(T.root[0],1,n);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&v,&pd,&pos);
    		if(pd==1)
    		{
    			scanf("%d",&val);
    			T.update(T.root[v],T.root[i],1,n,pos,val);
    		}
    		else
    		{
    			T.root[i]=T.root[v];//没有修改直接继承根节点
    			printf("%d\n",T.query(T.root[v],1,n,pos));
    		}
    	}
    	return 0;
    }
    

7.14

闲话

  • 下午去配了眼镜。
  • 听牛客的课。

做题纪要

luogu P3834 【模板】可持久化线段树 2

  • 多倍经验: SP3946 MKTHNUM - K-th Number | luogu P1533 可怜的狗狗

  • 主席树板子。

    • 对于 \([1,i]\) 均建立一棵权值线段树,称为第 \(i\) 版本线段树,利用可持久化的特性相同节点直接延用。
    • \([l,r]\) 差分成 \([1,r]\) 减去 \([1,l-1]\) ,在线段树上实现相减(同时访问两棵线段树将所有对应节点的权值相减即可实现),然后就是正常的线段树上二分过程。
  • 离散化来减小常数。

    点击查看代码
    int a[200010],b[200010];
    struct PDS_SMT
    {
    	int root[200010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[200010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;//手动进行 pushup 来减小常数
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	int query(int rt1,int rt2,int l,int r,int k)
    	{
    		if(l==r)
    		{
    			return l;
    		}
    		int mid=(l+r)/2,sum=tree[lson(rt2)].sum-tree[lson(rt1)].sum;//对应节点相减
    		if(k<=sum)
    		{
    			return query(lson(rt1),lson(rt2),l,mid,k);
    		}
    		else
    		{
    			return query(rson(rt1),rson(rt2),mid+1,r,k-sum);
    		}
    	}
    }T;
    int main()
    {
    	int n,m,l,r,k,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		b[i]=a[i];
    	}
    	sort(b+1,b+1+n);
    	b[0]=unique(b+1,b+1+n)-(b+1);
    	T.build_tree(T.root[0],1,b[0]);
    	for(i=1;i<=n;i++)
    	{
    		T.update(T.root[i-1],T.root[i],1,b[0],lower_bound(b+1,b+1+b[0],a[i])-b);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r>>k;
    		cout<<b[T.query(T.root[l-1],T.root[r],1,b[0],k)]<<endl;
    	}
    	return 0;
    }
    

[ABC362G] Count Substring Query

  • 多倍经验: luogu P5357 【模板】AC 自动机

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[6000000];
    int head[6000000],trie[6000000][30],fail[6000000],flag[6000000],id[6000000],din[6000000],lazy[6000000],ans[6000000],tot=0,cnt=0;
    char s[6000000],t[6000000];
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    int val(char x)
    {
    	return x-'a'+1;
    }
    void insert(char s[],int len,int idx)
    {
    	int x=0,i;
    	for(i=1;i<=len;i++)
    	{
    		if(trie[x][val(s[i])]==0)
    		{
    			tot++;
    			trie[x][val(s[i])]=tot;
    		}
    		x=trie[x][val(s[i])];
    	}
    	flag[x]=(flag[x]==0)?idx:flag[x];
    	id[idx]=flag[x];
    }
    void build()
    {
    	int x,i;
    	queue<int>q;
    	for(i=1;i<=26;i++)
    	{
    		if(trie[0][i]!=0)
    		{
    			fail[trie[0][i]]=0;
    			q.push(trie[0][i]);
    		}
    	}
    	while(q.empty()==0)
    	{
    		x=q.front();
    		q.pop();
    		for(i=1;i<=26;i++)
    		{   
    			if(trie[x][i]==0)
    			{
    				trie[x][i]=trie[fail[x]][i];
    			}
    			else
    			{
    				fail[trie[x][i]]=trie[fail[x]][i];
    				add(trie[x][i],fail[trie[x][i]]);
    				din[fail[trie[x][i]]]++;
    				q.push(trie[x][i]);
    			}
    		}
    	}
    }
    void mark(char s[],int len)
    {
    	int x=0,i;
    	for(i=1;i<=len;i++)
    	{
    		x=trie[x][val(s[i])];
    		lazy[x]++;
    	}
    }
    void top_sort()
    {
    	int x,i;
    	queue<int>q;
    	for(i=1;i<=tot;i++)
    	{
    		if(din[i]==0)
    		{
    			q.push(i);
    		}
    	}
    	while(q.empty()==0)
    	{
    		x=q.front();
    		q.pop();
    		ans[flag[x]]=lazy[x];
    		for(i=head[x];i!=0;i=e[i].nxt)
    		{
    			lazy[e[i].to]+=lazy[x];
    			din[e[i].to]--;
    			if(din[e[i].to]==0)
    			{
    				q.push(e[i].to);
    			}
    		}
    	}
    }
    int main()
    {
    	int n,i;
    	scanf("%s",s+1);
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		scanf("%s",t+1);
    		insert(t,strlen(t+1),i);
    	}
    	build();
    	mark(s,strlen(s+1));
    	top_sort();
    	for(i=1;i<=n;i++)
    	{
    		cout<<ans[id[i]]<<endl;
    	}
    	return 0;
    }
    

[ABC362D] Shortest Path 3

  • 由于点权是会重复运输的,所以在 \(dijkstra\) 过程中将点权和边权混着转移是正确的。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[400010];
    ll head[400010],dis[400010],vis[400010],a[400010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].w=w;
    	head[u]=cnt;
    }
    void dijkstra(ll s)
    {
    	ll x,i;
    	memset(vis,0,sizeof(vis));
    	memset(dis,0x3f,sizeof(dis));
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=a[s];
    	q.push(make_pair(-dis[s],-s));
    	while(q.empty()==0)
    	{
    		x=-q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w+a[e[i].to])
    				{
    					dis[e[i].to]=dis[x]+e[i].w+a[e[i].to];
    					q.push(make_pair(-dis[e[i].to],-e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	ll n,m,u,v,w,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		add(u,v,w);
    		add(v,u,w);
    	}
    	dijkstra(1);
    	for(i=2;i<=n;i++)
    	{
    		cout<<dis[i]<<" ";
    	}
    	return 0;
    }
    

SP14887 GOODA - Good Travels

  • 缩点后一定是有向无环图,此时在 \(dijkstra\) 过程中将点权和边权混着转移是正确的。

    点击查看代码
    struct node
    {
    	ll nxt,to;
    }e[1000010];
    stack<ll>s;
    ll head[1000010],dfn[1000010],low[1000010],ins[1000010],c[1000010],a[1000010],b[1000010],u[1000010],v[1000010],vis[1000010],dis[1000010],cnt=0,tot=0,ans=0;
    void add(ll u,ll v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void tarjan(ll x)
    {
    	ll k=0,i;
    	tot++;
    	dfn[x]=low[x]=tot;
    	ins[x]=1;
    	s.push(x);
    	for(i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(dfn[e[i].to]==0)
    		{
    			tarjan(e[i].to);
    			low[x]=min(low[x],low[e[i].to]);
    		}
    		else
    		{
    			if(ins[e[i].to]==1)
    			{
    				low[x]=min(low[x],dfn[e[i].to]);
    			}
    		}
    	}
    	if(dfn[x]==low[x])
    	{
    		ans++;
    		while(x!=k)
    		{
    			k=s.top();
    			ins[k]=0;
    			c[k]=ans;
    			b[ans]+=a[k];
    			s.pop();
    		}
    	}
    }
    void dijkstra(ll st)
    {
    	ll x,i;
    	memset(vis,0,sizeof(vis));
    	memset(dis,-0x3f,sizeof(dis));
    	priority_queue<pair<ll,ll> >q;
    	dis[st]=b[st];
    	q.push(make_pair(dis[st],st));
    	while(q.empty()==0)
    	{
    		x=q.top().second;
    		q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]<dis[x]+b[e[i].to])
    				{
    					dis[e[i].to]=dis[x]+b[e[i].to];
    					q.push(make_pair(dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    }
    int main()
    {
    	ll n,m,st,ed,i;
    	cin>>n>>m>>st>>ed;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>u[i]>>v[i];
    		add(u[i],v[i]);
    	}
    	for(i=1;i<=n;i++)
    	{
    		if(dfn[i]==0)
    		{
    			tarjan(i);
    		}
    	}
    	cnt=0;
    	memset(e,0,sizeof(e));
    	memset(head,0,sizeof(head));
    	for(i=1;i<=m;i++)
    	{
    		if(c[u[i]]!=c[v[i]])
    		{
    			add(c[u[i]],c[v[i]]);
    		}
    	}
    	dijkstra(c[st]);
    	cout<<dis[c[ed]]<<endl;
    	return 0;
    }
    

luogu P2188 小Z的 k 紧凑数

  • 记忆化搜索。

    点击查看代码
    ll a[20],f[20][20];
    ll divide(ll n,ll a[])
    {
    	ll len=0;
    	while(n)
    	{
    		len++;
    		a[len]=n%10;
    		n/=10;
    	}
    	return len;
    }
    ll dfs(ll pos,ll pre,ll lead,ll limit,ll k)
    {
    	if(pos<=0)
    	{
    		return 1;
    	}
    	if(f[pos][pre]!=-1&&lead==0&&limit==0)
    	{
    		return f[pos][pre];
    	}
    	ll ans=0,maxx=(limit==0)?9:a[pos],i;
    	for(i=0;i<=maxx;i++)
    	{
    		if(lead==1||abs(pre-i)<=k)
    		{
    			ans+=dfs(pos-1,i,(i==0)*lead,(i==maxx)*limit,k);	
    		}
    	}
    	return (lead==0&&limit==0)?f[pos][pre]=ans:ans;
    }
    ll ask(ll n,ll k)
    {
    	ll len=divide(n,a);
    	return dfs(len,0,1,1,k);
    }
    int main()
    {
    	ll l,r,k;
    	cin>>l>>r>>k;
    	memset(f,-1,sizeof(f));
    	cout<<ask(r,k)-ask(l-1,k)<<endl;
    	return 0;
    }
    

7.15

闲话

  • 上午把初中的书收拾了收拾。
  • 听牛客的课。

做题纪要

luogu P5569 [SDOI2008] 石子合并

  • Garsia–Wachs 算法 板子。

    • 算法流程过于抽象,不再展开详细说明。
  • \(a_{0}=a_{n+1}= \infty\) ,每次在 \([1,n]\) 找到一个最小的 \(pos\) 使得 \(a_{pos-1} \le a_{pos+1}\) ,接着在 \([0,pos-2]\) 中找到一共最大的 \(i\) 使得 \(a_{i}>a_{pos-1}+a_{pos}\) ,删除 \(a_{pos-1},a_{pos}\) 后将 \(a_{pos-1}+a_{pos}\) 插至 \(i\) 右边。

  • 时间复杂度因为 vector 插入和删除的小常数所以可以过,但按理来说应该写两棵平衡树来维护。

  • 数据加强版详见 AT_atc002_c 最適二分探索木 | POJ1738 An old Stone Game

    点击查看代码
    vector<int>a;
    int main()
    {
    	int n,x,pos,num,ans=0,i;
    	cin>>n;
    	a.push_back(0x7f7f7f7f);
    	for(i=1;i<=n;i++)
    	{
    		cin>>x;
    		a.push_back(x);
    	}
    	a.push_back(0x7f7f7f7f);
    	while(a.size()>=4)
    	{
    		pos=1;
    		while(a[pos-1]>a[pos+1]&&pos<=n)
    		{
    			pos++;
    		}
    		ans+=a[pos-1]+a[pos];
    		num=a[pos-1]+a[pos];
    		for(i=pos-1;i>=0;i--)
    		{
    			if(a[i]>num)
    			{
    				break;
    			}
    		}
    		a.erase(a.begin()+pos-1);
    		a.erase(a.begin()+pos-1);
    		a.insert(a.begin()+i+1,num);
    	}
    	cout<<ans<<endl;
    	return 0;
    }	
    

luogu P1912 [NOI2009] 诗人小G

[ABC339G] Smaller Sum

  • 分块板子。排序后维护块内前缀和。

  • 块长为 \(\sqrt{n \log n}\)

    点击查看代码
    ll a[200010],b[200010],L[200010],R[200010],pos[200010],sum[200010],klen,ksum;
    void init(ll n)
    {
    	klen=sqrt(n*log2(n));
    	ksum=n/klen;
    	for(ll i=1;i<=ksum;i++)
    	{
    		L[i]=R[i-1]+1;
    		R[i]=R[i-1]+klen;
    	}
    	if(R[ksum]<n)
    	{
    		ksum++;
    		L[ksum]=R[ksum-1]+1;
    		R[ksum]=n;
    	}
    	for(ll i=1;i<=ksum;i++)
    	{
    		for(ll j=L[i];j<=R[i];j++)
    		{
    			pos[j]=i;
    		}
    		sort(b+L[i],b+R[i]+1);
    		sum[L[i]]=b[L[i]];
    		for(ll j=L[i]+1;j<=R[i];j++)
    		{
    			sum[j]=sum[j-1]+b[j];
    		}
    	}
    }
    ll query(ll l,ll r,ll val)
    {
    	ll ans=0;
    	if(pos[l]==pos[r])
    	{
    		for(ll i=l;i<=r;i++)
    		{
    			ans+=(a[i]<=val)*a[i];
    		}
    	}
    	else
    	{
    		for(ll i=l;i<=R[pos[l]];i++)
    		{
    			ans+=(a[i]<=val)*a[i];
    		}
    		for(ll i=pos[l]+1;i<=pos[r]-1;i++)
    		{
    			if(b[L[i]]<=val)
    			{
    				ans+=sum[upper_bound(b+L[i],b+R[i]+1,val)-b-1];
    			}
    		}
    		for(ll i=L[pos[r]];i<=r;i++)
    		{
    			ans+=(a[i]<=val)*a[i];
    		}
    	}
    	return ans;
    }
    int main()
    {
    	ll n,q,l,r,val,ans=0,i;
    	scanf("%lld",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&a[i]);
    		b[i]=a[i];
    	}
    	init(n);
    	scanf("%lld",&q);
    	for(i=1;i<=q;i++)
    	{
    		scanf("%lld%lld%lld",&l,&r,&val);
    		l^=ans;
    		r^=ans;
    		val^=ans;
    		ans=query(l,r,val);
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    

7.16

闲话

做题纪要

luogu P3243 [HNOI2015] 菜肴制作

  • 要求字典序最小是有其他条件限制的,故单纯的求字典序最小拓扑序的做法是假的。

  • 而后面的点编号越大一定越优,转化为求反转后字典序最大的拓扑序。

  • 反向建图优先队列维护字典序最大的拓扑序即可。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[100010];
    int head[100010],ans[100010],din[100010],cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    bool top_sort(int n)
    {
    	priority_queue<int>q;
    	int x,len=0,i;
    	for(i=1;i<=n;i++)
    	{
    		if(din[i]==0)
    		{
    			q.push(i);
    		}
    	}
    	while(q.empty()==0)
    	{
    		x=q.top();
    		q.pop();
    		len++;
    		ans[len]=x;
    		for(i=head[x];i!=0;i=e[i].nxt)
    		{
    			din[e[i].to]--;
    			if(din[e[i].to]==0)
    			{
    				q.push(e[i].to);
    			}
    		}
    	}
    	return len==n;
    }
    int main()
    {   
    	int t,n,m,u,v,i,j;
    	cin>>t;
    	for(j=1;j<=t;j++)
    	{
    		cnt=0;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(din,0,sizeof(din));
    		cin>>n>>m;
    		for(i=1;i<=m;i++)
    		{
    			cin>>v>>u;
    			add(u,v);
    			din[v]++;
    		}
    		if(top_sort(n)==false)
    		{
    			cout<<"Impossible!"<<endl;
    		}
    		else
    		{
    			for(i=n;i>=1;i--)
    			{
    				cout<<ans[i]<<" ";
    			}
    			cout<<endl;
    		}
    	}
    	return 0;
    }
    

牛客 NC275607 小红的同余

  • \(2x=km+1(k \bmod 2=1)\) ,又因为 \(x \in [0,m)\)\(2x \in [0,2m)\) ,那么 \(k\) 只能取 \(1\)

  • 最终有 \(x=\frac{m+1}{2}\) 即为所求。

    点击查看代码
    int main()
    {
    	ll m;
    	cin>>m;
    	cout<<(m+1)/2<<endl;
    	return 0;
    }
    

牛客 NC275612 小红的三倍数

  • 拼接顺序并不影响最终结果。

  • \(x \bmod 3=0(x \in \mathbb{N})\) 当且仅当 \(x\) 在十进制下各数位的数码之和 \(\bmod 3=0\)

    点击查看代码
    char a[200];
    int main()
    {
    	ll n,sum=0,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>(a+1);
    		for(j=1;j<=strlen(a+1);j++)
    		{
    			sum=(sum+a[j]-'0')%3;
    		}
    	}
    	if(sum==0)
    	{
    		cout<<"YES"<<endl;
    	}
    	else
    	{
    		cout<<"NO"<<endl;
    	}
    	return 0;
    }
    

牛客 NC275615 小红的 gcd

  • 杀鸡焉用牛刀。

  • \(\gcd(a,b)=\gcd(b,a \bmod b)\) ,将 \(a\) 缩小至 \([0,b)\) 后计算即可。

    点击查看代码
    char s[1000010];
    ll gcd(ll a,ll b)
    {
    	return b?gcd(b,a%b):a;
    }
    int main()
    {
    	ll a=0,b,i;
    	cin>>(s+1)>>b;
    	for(i=1;i<=strlen(s+1);i++)
    	{
    		a=(a*10%b+s[i]-'0')%b;
    	}
    	cout<<gcd(a,b)<<endl;
    	return 0;
    }
    

牛客 NC275626 小红的数组

  • 初三奥赛模拟测试4 T3 渡尘

    点击查看代码
    ll a[500010],sum[500010],fmaxx[500010][25],fminn[500010][25];
    void init(ll n,ll a[])
    {
    	memset(fminn,0x3f,sizeof(fminn));
    	for(ll i=1;i<=n;i++)
    	{
    		fminn[i][0]=fmaxx[i][0]=a[i];
    	}
    	for(ll j=1;j<=log2(n);j++)
    	{
    		for(ll i=1;i<=n-(1<<j)+1;i++)
    		{
    			fmaxx[i][j]=max(fmaxx[i][j-1],fmaxx[i+(1<<(j-1))][j-1]);
    			fminn[i][j]=min(fminn[i][j-1],fminn[i+(1<<(j-1))][j-1]);
    		}
    	}
    }
    ll query_max(ll l,ll r)
    {
    	ll t=log2(r-l+1);
    	return max(fmaxx[l][t],fmaxx[r-(1<<t)+1][t]);
    }
    ll query_min(ll l,ll r)
    {
    	ll t=log2(r-l+1);
    	return min(fminn[l][t],fminn[r-(1<<t)+1][t]);
    }
    int main()
    {
    	ll n,m,l,r,i;
    	scanf("%lld",&n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%lld",&a[i]);
    		sum[i+1]=sum[i]+a[i];
    	}
    	scanf("%lld",&m);
    	init(n+1,sum);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%lld%lld",&l,&r);
    		r++;
    		printf("%lld\n",query_max(l,r)-query_min(l,r));
    	}
    	return 0;
    }
    

7.17

闲话

  • 听牛客的课。
  • 下午 \(6:00\) 进校。进校后先把行李放在了尚贤楼 \(1\) 楼,然后带着 @Charlie_ljk 和另一个外校(据他所说,最近上过公益课)的学生去了机房,看了下宿舍安排加签到。被分到了 \(413\) 下铺,然后回原宿舍搬行李,并成功地从另一个门进入了原宿舍楼( \(field\) 称原宿舍楼暂时进不去),等我行李都收拾好了 \(field\) 才从打不开的门里进来了,还问我是怎么进来的。
  • 新宿舍条件还好,一屋住 \(6\) 人,每人两个柜子和一个箱子,没有电扇,有空调、浴室、厕所,有饮水机但水流速度极慢,公厕较为干净。床不是很结实,翻个身就嘎吱嘎吱响,床下柜子的配置和升初一那个暑假去 JC 夏令营的配置一样。
  • 晚上 \(field\) 给众人讲了下作息,要求和高三的时间一样,和原来不同的是早饭早了 \(15 \min\) 和晚饭延后了 \(5 \min\) ;跟我们说集训期间也要注意纪律,本部领导有点多,别再整得“奥赛生注意点纪律,特别是信奥的”;把时间表打印了一份贴到了门上,算是兑换了他曾经的话。
    • \(miaomiao\) 不在山, \(field\) 称大王。
  • 晚上让 \(21:30\) 回宿舍。

做题纪要

牛客 NC275621 小红走矩阵

  • 二分答案加 \(BFS\)

    点击查看代码
    int a[510][510],vis[510][510],dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
    bool bfs(int sx,int sy,int ex,int ey,int mid,int n,int m)
    {
    	queue<pair<int,int> >q;
    	memset(vis,0,sizeof(vis));
    	if(a[sx][sy]<=mid)
    	{
    		q.push(make_pair(sx,sy));
    		vis[sx][sy]=1;
    	}
    	while(q.empty()==0)
    	{
    		int x=q.front().first,y=q.front().second;
    		q.pop();
    		if(x==ex&&y==ey)
    		{
    			return true;
    		}
    		for(int i=0;i<=3;i++)
    		{
    			int nx=x+dx[i],ny=y+dy[i];
    			if(1<=nx&&nx<=n&&1<=ny&&ny<=m&&vis[nx][ny]==0&&a[nx][ny]<=mid)
    			{
    				q.push(make_pair(nx,ny));
    				vis[nx][ny]=1;
    			}
    		}
    	}
    	return false;
    }
    bool check(int mid,int n)
    {
    	return bfs(1,1,n,n,mid,n,n);
    }
    int main()
    {
    	int n,l=1,r=0,mid,ans,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    		{
    			cin>>a[i][j];
    			r=max(r,a[i][j]);
    		}
    	}
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n)==true)
    		{
    			ans=mid;
    			r=mid-1;
    		}
    		else
    		{
    			l=mid+1;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

牛客 NC275609 小红充电

  • 可以通过耗电回到超级快充状态。

    点击查看代码
    int main()
    {
    	double x,y,t,a,b,c;
    	cin>>x>>y>>t>>a>>b>>c;
    	if(x<=t)
    	{
    		printf("%.8lf\n",(100-x)/c);
    	}
    	else
    	{	
    		printf("%.8lf\n",min((100-x)/b,(x-t)/y+(100-t)/c));
    	}
    	return 0;
    }
    

luogu P4028 New Product

  • \(BSGS\) 板子。

  • 得到的解要符合实际意义,要保证最后必须有 \(b\) 个剩余。故当 \(\begin{cases} a \equiv 0 \pmod{p} \\ b \ne 0 \end{cases}\) 时无解。

    点击查看代码
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=ans*a%p;
    		}
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    ll bsgs(ll a,ll b,ll p)
    {
    	if(1%p==b%p)
    	{
    		return 0;
    	}
    	else
    	{
    		map<ll,ll>vis;
    		ll k=sqrt(p)+1,i,sum;
    		for(i=0;i<=k-1;i++)
    		{
    			vis[b*qpow(a,i,p)%p]=i;
    		}
    		a=qpow(a,k,p);
    		for(i=0;i<=k;i++)
    		{
    			sum=qpow(a,i,p);
    			if(vis.find(sum)!=vis.end())
    			{
    				if(i*k-vis[sum]>=0)
    				{
    					return i*k-vis[sum];
    				}
    			}
    		}
    		return -1;
    	}
    }
    int main()
    {
    	ll t,p,a,b,ans,i;
    	cin>>t;
    	for(i=1;i<=t;i++)
    	{
    		cin>>p>>a>>b;
    		if(a%p==0&&b!=0)
    		{
    			cout<<"Couldn't Produce!"<<endl;
    		}
    		else
    		{
    			ans=bsgs(a,b,p);
    			if(ans==-1)
    			{
    				cout<<"Couldn't Produce!"<<endl;
    			}
    			else
    			{
    				cout<<ans<<endl;
    			}
    		}
    	}
    	return 0;
    }
    

luogu P10417 [蓝桥杯 2023 国 A] 第 K 小的和

  • 类似 luogu P1631 序列合并 进行暴力合并会 \(MLE\)

  • 考虑先将 \(\{ a \},\{ b \}\) 进行排序,接着二分答案,依据 \(\le\) 二分出的答案的数的个数和 \(k\) 的大小进行判定。

    点击查看代码
    ll a[100010],b[100010];
    bool check(ll mid,ll n,ll m,ll k)
    {
    	ll sum=0,i;
    	for(i=1;i<=n;i++)
    	{
    		sum+=upper_bound(b+1,b+1+m,mid-a[i])-1-b;
    	}
    	return sum>=k;
    }
    int main()
    {
    	ll n,m,k,l=1,r=0,mid,ans=0,i,j;
    	cin>>n>>m>>k;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+1+n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>b[i];
    	}
    	sort(b+1,b+1+m);
    	r=a[n]+b[m];
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n,m,k)==true)
    		{
    			ans=mid;
    			r=mid-1;
    		}
    		else
    		{
    			l=mid+1;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

7.18

闲话

  • 上午 \(7:30 \sim 11:30\) @Delov 学长安排了一场模拟赛。
  • 中午 \(feifei\) 查宿。
  • 下午让去第四机房组织线下讲题。讲完题 @Delov 学长给说了下暑假集训安排,概况来说就是时间紧,任务重,前几天过知识点,模拟赛和加赛居多。
  • 晚上想打 \(CF\)\(feifie\)\(huge,miaomiao\) 觉得最近几天任务重,要让我们好好养护精神,所以没让打。
  • 晚上临下课的时候被 \(feifei\) 拉去 \(504\) 开小班会,课件和大纲都是临时准备的。回来吧,我的 \(bobo\)

做题纪要

luogu P2633 Count on a tree

  • 多倍经验: SP10628 COT - Count on a tree

  • \(u \to v\) 差分成 \((rt \to u)+(rt \to v)-(rt \to lca(u,v))-(rt \to fa(lca(u,v)))\) 。询问时同时查询 \(4\) 条线段即可。

  • 主席树维护即可。

    点击查看代码
    struct PDS_SMT
    {
        int root[200010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,sum;
        }tree[200010<<5];
        #define lson(rt) tree[rt].ls
        #define rson(rt) tree[rt].rs
        int build_rt()
        {
            rt_sum++;
            return rt_sum;
        }
        void build_tree(int &rt,int l,int r)
        {
            rt=build_rt();
            if(l==r)
            {
                return;
            }
            int mid=(l+r)/2;
            build_tree(lson(rt),l,mid);
            build_tree(rson(rt),mid+1,r);
        }
        void update(int pre,int &rt,int l,int r,int pos)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            tree[rt].sum++;
            if(l==r)
            {
                return;
            }
            int mid=(l+r)/2;
            if(pos<=mid)
            {
                update(lson(pre),lson(rt),l,mid,pos);
            }
            else
            {
                update(rson(pre),rson(rt),mid+1,r,pos);
            }
        }
        int query(int rt1,int rt2,int rt3,int rt4,int l,int r,int k)
        {
            if(l==r)
            {
                return l;
            }
            int mid=(l+r)/2,sum=tree[lson(rt1)].sum+tree[lson(rt2)].sum-tree[lson(rt3)].sum-tree[lson(rt4)].sum;
            if(k<=sum)
            {
                return query(lson(rt1),lson(rt2),lson(rt3),lson(rt4),l,mid,k);
            }
            else
            {
                return query(rson(rt1),rson(rt2),rson(rt3),rson(rt4),mid+1,r,k-sum);
            }
        }
    }T;
    struct node
    {
        int nxt,to;
    }e[200010];
    int head[200010],fa[200010][20],dep[200010],a[200010],b[200010],cnt=0,N;
    void add(int u,int v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs(int x,int father)
    {
        fa[x][0]=father;
        dep[x]=dep[father]+1;
        T.update(T.root[father],T.root[x],1,b[0],lower_bound(b+1,b+1+b[0],a[x])-b);
        for(int i=1;(1<<i)<=dep[x];i++)
        {
            fa[x][i]=fa[fa[x][i-1]][i-1];
        }
        for(int i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=father)
            {
                dfs(e[i].to,x);
            }
        }
    }
    int lca(int x,int y)
    {
        if(dep[x]>dep[y])
        {
            swap(x,y);
        }
        for(int i=N;i>=0;i--)
        {
            if(dep[x]+(1<<i)<=dep[y])
            {
                y=fa[y][i];
            }
        }
        if(x==y)
        {
            return x;
        }
        else
        {
            for(int i=N;i>=0;i--)
            {
                if(fa[x][i]!=fa[y][i])
                {
                    x=fa[x][i];
                    y=fa[y][i];
                }
            }
            return fa[x][0];
        }
    }
    int main()
    {
        int n,m,u,v,k,rt,last=0,i;
        cin>>n>>m;
        N=log2(n)+1;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            b[i]=a[i];
        }
        sort(b+1,b+1+n);
        b[0]=unique(b+1,b+1+n)-(b+1);
        T.build_tree(T.root[0],1,b[0]);
        for(i=1;i<=n-1;i++)
        {
            cin>>u>>v;
            add(u,v);
            add(v,u);
        }
        dfs(1,0);
        for(i=1;i<=m;i++)
        {
            cin>>u>>v>>k;
            u^=last;
            rt=lca(u,v);
            last=b[T.query(T.root[u],T.root[v],T.root[rt],T.root[fa[rt][0]],1,b[0],k)];
            cout<<last<<endl;
        }
        return 0;
    }
    

luogu P1972 [SDOI2009] HH的项链

  • 懒得挂多倍经验了。

  • 令所有种类的贝壳都在 \(0\) 的位置上出现了一次。

  • 主席树维护 \([l,r]\) 中的种类以前出现位置在 \([0,l)\) 的种类数即可。

    点击查看代码
    int a[1000010],last[1000010];
    struct PDS_SMT
    {
    	int root[1000010],rt_sum=0;
    	struct SegmentTree
    	{
    		int ls,rs,sum;
    	}tree[1000010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;
    		if(l==r)
    		{
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	int query(int rt1,int rt2,int l,int r,int x,int y)
    	{
    		if(x<=l&&r<=y)
    		{
    			return tree[rt2].sum-tree[rt1].sum;	
    		}
    		int mid=(l+r)/2,ans=0;
    		if(x<=mid)
    		{
    			ans+=query(lson(rt1),lson(rt2),l,mid,x,y);
    		}
    		if(y>mid)
    		{
    			ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y);
    		}
    		return ans;
    	}
    }T;
    int main()
    {
    	int n,m,u,v,ans=0,i;
    	scanf("%d",&n);
    	T.build_tree(T.root[0],0,n);
    	for(i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		T.update(T.root[i-1],T.root[i],0,n,last[a[i]]);
    		last[a[i]]=i;	
    	}
    	scanf("%d",&m);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&u,&v);
    		ans=T.query(T.root[u-1],T.root[v],0,n,0,u-1);
    		printf("%d\n",ans);
    	}
    	return 0;
    }
    

CF404D Minesweeper 1D

T2790. 小凯的疑惑

luogu P5290 [十二省联考 2019] 春节十二响

7.19

闲话

做题纪要

luogu P7506 「Wdsr-2.5」琪露诺的算数游戏

LibreOJ 6095. 花神的嘲讽计划

  • 预处理出所有长度为 \(k\) 的子串的哈希值,然后就转化成了查询区间内点是否出现过。

  • 离散化后主席树维护即可。

    点击查看代码
    const ull base=285714287;
    ull s[100010],jc[100010],a[100010],b[200010],c[200010],l[100010],r[100010],x[100010];
    void sx_hash(ull s[],ull a[],ull len)
    {
    	for(ull i=0;i<=len;i++)
    	{
    		a[i]=(i==0)?0:a[i-1]*base+s[i];
    	}
    }
    ull ask_hash(ull a[],ull l,ull r)
    {
    	return a[r]-a[l-1]*jc[r-l+1];
    }
    struct PDS_SMT
    {
    	ull root[100010],rt_sum=0;
    	struct SegmentTree
    	{
    		ull ls,rs,sum;
    	}tree[100010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	ull build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void build_tree(ull &rt,ull l,ull r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			return;
    		}
    		ull mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    	}
    	void update(ull pre,ull &rt,ull l,ull r,ull pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		tree[rt].sum++;
    		if(l==r)
    		{
    			return;
    		}
    		ull mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    	}
    	ull query(ull rt1,ull rt2,ull l,ull r,ull pos)
    	{
    		if(rt2==0)
    		{
    			return 0;
    		}
    		if(l==r)
    		{
    			return tree[rt2].sum-tree[rt1].sum;
    		}
    		ull mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			return query(lson(rt1),lson(rt2),l,mid,pos);
    		}
    		else
    		{
    			return query(rson(rt1),rson(rt2),mid+1,r,pos);
    		}
    	}
    }T;
    int main()
    {
    	ull n,m,k,i,j;
    	cin>>n>>m>>k;
    	for(i=0;i<=n;i++)
    	{
    		jc[i]=(i==0)?1:jc[i-1]*base;
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>s[i];
    	}
    	sx_hash(s,a,n);
    	for(i=1;i+k-1<=n;i++)
    	{
    		b[0]++;
    		b[b[0]]=c[b[0]]=ask_hash(a,i,i+k-1);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>l[i]>>r[i];
    		for(j=1;j<=k;j++)
    		{
    			cin>>s[j];
    		}
    		sx_hash(s,a,k);
    		b[0]++;
    		b[b[0]]=c[b[0]]=ask_hash(a,1,k);
    	}
    	sort(b+1,b+1+b[0]);
    	b[0]=unique(b+1,b+1+b[0])-(b+1);	
    	T.build_tree(T.root[0],1,b[0]);
    	for(i=1;i+k-1<=n;i++)
    	{
    		T.update(T.root[i-1],T.root[i],1,b[0],lower_bound(b+1,b+1+b[0],c[i])-b);
    	}
    	for(i=1;i<=m;i++)
    	{
    		if(T.query(T.root[l[i]-1],T.root[r[i]-k+1],1,b[0],lower_bound(b+1,b+1+b[0],c[i+n-k+1])-b)==0)
    		{
    			cout<<"Yes"<<endl;
    		}
    		else
    		{
    			cout<<"No"<<endl;
    		}
    	}
    	return 0;
    }
    

POJ1655 Balancing Act

  • 树的重心板子。

    • 如果在树上选择某个节点并将其删除,这棵树将分成若干棵子树,使这些子树中最大的一棵的大小取到最小值的节点被称为树的重心。
    • 性质
      • 树的重心若不唯一,则至多有两个,且这两个重心相邻。
      • 以树的重心作为整棵树的根节点时,所有子树的大小均不超过整棵树大小的一半。
        • 常也把这条性质作为重心的定义。
      • 树中所有点到某个点的距离和中,到重心的距离和是最小的;如果有两个重心,那么到它们的距离和一样。
      • 把两棵树通过一条边相连得到一棵新的树,那么新的树的重心在连接原来两棵树的重心的路径上。
    • 求法
      • 根据定义判断。
    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[400010];
    int head[400010],siz[400010],weight[400010],center=0,cnt=0;
    void add(int u,int v)
    {
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	head[u]=cnt;
    }
    void dfs(int x,int fa,int n)
    {
    	siz[x]=1;
    	weight[x]=0;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa)
    		{
    			dfs(e[i].to,x,n);
    			siz[x]+=siz[e[i].to];
    			weight[x]=max(weight[x],siz[e[i].to]);//最大子树大小
    		}
    	}
    	weight[x]=max(weight[x],n-siz[x]);
    	if(weight[x]<=n/2)
    	{
    		center=(center==0)?x:min(center,x);//要求较小的那一个
    	}
    }
    int main()
    {
    	int t,n,u,v,i,j;
    	cin>>t;
    	for(i=1;i<=t;i++)
    	{
    		cin>>n;
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		cnt=center=0;
    		for(j=1;j<=n-1;j++)
    		{
    			cin>>u>>v;
    			add(u,v);
    			add(v,u);
    		}
    		dfs(1,0,n);
    		cout<<center<<" "<<weight[center]<<endl;
    	}
    	return 0;
    }
    

CF786B Legacy

luogu P2839 [国家集训队] middle

  • \([l,r]\) 的中位数为 \(mid\) ,将 \([l,r]\)\(\ge mid\) 的记为 \(1\)\(< mid\) 的记为 \(-1\) ,则有 \([l,r]\) 的和 \(\ge 0\)

  • 二分最终的答案下标,对每个答案下标建一棵线段树。

  • \(\{ a \}\) 进行排序,相邻两数与其他数仅有小的那个数的位置上由 \(1\) 变成了 \(-1\) 。主席树维护即可。

  • 处理询问同 SP2916 GSS5 - Can you answer these queries V ,处理出最大左/右子段和,把式子拆开就行了。

    点击查看代码
    struct PDS_SMT
    {
    	int root[20010],rt_sum;
    	struct SegmentTree
    	{
    		int ls,rs,maxl,maxr,sum;
    	}tree[20010<<5];
    	#define lson(rt) tree[rt].ls
    	#define rson(rt) tree[rt].rs
    	int build_rt()
    	{
    		rt_sum++;
    		return rt_sum;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    		tree[rt].maxl=max(tree[lson(rt)].sum+tree[rson(rt)].maxl,tree[lson(rt)].maxl);
    		tree[rt].maxr=max(tree[rson(rt)].sum+tree[lson(rt)].maxr,tree[rson(rt)].maxr);
    	}
    	void build_tree(int &rt,int l,int r)
    	{
    		rt=build_rt();
    		if(l==r)
    		{
    			tree[rt].maxl=tree[rt].maxr=tree[rt].sum=1;
    			return;
    		}
    		int mid=(l+r)/2;
    		build_tree(lson(rt),l,mid);
    		build_tree(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(int pre,int &rt,int l,int r,int pos)
    	{
    		rt=build_rt();
    		tree[rt]=tree[pre];
    		if(l==r)
    		{
    			tree[rt].maxl=tree[rt].maxr=tree[rt].sum=-1;
    			return;
    		}
    		int mid=(l+r)/2;
    		if(pos<=mid)
    		{
    			update(lson(pre),lson(rt),l,mid,pos);
    		}
    		else
    		{
    			update(rson(pre),rson(rt),mid+1,r,pos);
    		}
    		pushup(rt);
    	}
    	SegmentTree query(ll rt,ll l,ll r,ll x,ll y)
    	{
    		if(x<=l&&r<=y)
    		{
    			return tree[rt];
    		}
    		int mid=(l+r)/2;
    		if(y<=mid)
    		{
    			return query(lson(rt),l,mid,x,y);
    		}
    		else
    		{
    			if(x>mid)
    			{
    				return query(rson(rt),mid+1,r,x,y);
    			}
    			else
    			{
    				SegmentTree p=query(lson(rt),l,mid,x,y),q=query(rson(rt),mid+1,r,x,y),num;
    				num.sum=p.sum+q.sum;
    				num.maxl=max(p.sum+q.maxl,p.maxl);
    				num.maxr=max(q.sum+p.maxr,q.maxr);
    				return num;
    			}
    		}
    	}
    }T;
    struct node
    {
    	int pos,val;
    }a[20010];
    int q[10];
    bool cmp(node a,node b)
    {
    	return (a.val==b.val)?(a.pos<b.pos):(a.val<b.val);
    }
    bool check(int mid,int a,int b,int c,int d,int n)
    {
    	return T.query(T.root[mid],1,n,a,b).maxr+T.query(T.root[mid],1,n,b,c).sum+T.query(T.root[mid],1,n,c,d).maxl-T.query(T.root[mid],1,n,b,b).sum-T.query(T.root[mid],1,n,c,c).sum>=0;
    }
    int main()
    {
    	int n,m,last=0,l,r,mid,ans,i,j;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i].val;
    		a[i].pos=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	T.build_tree(T.root[1],1,n);//第一棵树全是 1
    	for(i=2;i<=n;i++)
    	{
    		T.update(T.root[i-1],T.root[i],1,n,a[i-1].pos);//因为后面二分能找到相邻多个元素相同的第一个位置,所以直接赋成-1是没有问题的
    	}
    	cin>>m;
    	for(i=1;i<=m;i++)
    	{
    		for(j=1;j<=4;j++)
    		{
    			cin>>q[j];
    			q[j]=(q[j]+last)%n+1;
    		}
    		sort(q+1,q+1+4);
    		l=1;
    		r=n;
    		ans=0;
    		while(l<=r)
    		{
    			mid=(l+r)/2;
    			if(check(mid,q[1],q[2],q[3],q[4],n)==true)
    			{
    				ans=mid;
    				l=mid+1;
    			}
    			else
    			{
    				r=mid-1;
    			}
    		}
    		last=a[ans].val;
    		cout<<last<<endl;
    	}
    	return 0;
    }
    

luogu P2397 yyy loves Maths VI (mode)

UVA1608 不无聊的序列 Non-boring sequences

7.20

闲话

  • 上午 @KafuuChinocpp 学长给讲了 K-D Tree ,替罪羊树,平面最近点对,圆方树,虚树,2-SAT,替罪羊树和虚树因为没学所以就现场讲了。
  • 讲完课之后 \(feifei\) 给发了雪糕,貌似是 @Really 学姐买的。然后 \(feifei\) 说了下下午体活时间为 \(18:00 \sim 19:00\) ,顺带把饭吃了,明天早上 \(7:00\) 到机房,体活时间不要在校园边缘溜达,被举报了只能去酒店集训了; \(field\) 说下周一会有省长来 HZ 视察,让我们躲着点走,如果我们遇到采访就收是暑假研学的,千万不要说是集训的。
  • 下午 \(14:00 \sim 18:00\) @joke3579 学长安排了一场模拟赛。
  • 吃完晚饭就直接来机房了,看到众学长在一起颓。 @Delov 学长还问我们为啥不去上体活。
  • 晚上讲题。

做题纪要

T2731. DP搬运工1

luogu P4145 上帝造题的七分钟 2 / 花神游历各国

[ARC111A] Simple Math 2

CF819B Mister B and PR Shifts

luogu P4062 [Code+#1] Yazid 的新生舞会

posted @ 2024-07-11 06:19  hzoi_Shadow  阅读(92)  评论(1编辑  收藏  举报
扩大
缩小