CSP-S模拟赛三(仍然是难度远超CSP-S)

前言

现在我将以最悲伤的心情,记录下我的第一次爆 000 记录。

T1

题面:

(我恨 MLE。)

对于 30%30\%30% 的数据,我们可以用 ST 表然后 O(n2)O(n^2)O(n2) 暴力枚举(我因为开了 long long,空间复杂度太高然后 MLE 了……)。

对于 100%100\%100% 的数据,考虑使用 O(n)O(n)O(n) 的做法。

我们美剧一个同学,把他作为一段区间中最矮的同学,然后看看以他为最矮的,最远能延伸到哪里。

但是如果按照上面的方法直接暴力做的话,时间复杂度就是 O(n2)O(n^2)O(n2) 的。有没有一种办法可以把里面的循环直接去掉呢?

既然要把里面的循环去掉,就说明查找这个区间所遍历的范围是固定的,那就定个很简单的值:相邻的两个。所以说当相邻的两个同学身高比中间这个同学的身高要高的话,就可以拓宽范围。

但如果我相邻的相邻的那个同学身高也比我高呢?那肯定也要合并进来。按照我们上面的操作,我相邻的同学的相邻的同学应该被囊括进了我相邻的那个同学,也就是说我在把我相邻的那个同学囊括进来的时候,还要把我相邻的相邻的那个同学囊括进来,那什么算法可以干这个事呢?对了:并查集

因此考虑使用并查集,因为我囊括的同学都是比我身高高的同学,所以我们应该按照身高从高到低的顺序依次用并查集。

然后就是写代码,代码自己看下面:

#include<bits/stdc++.h>
#define code using
#define by namespace
#define plh std
code by plh;
namespace fastio
{
	int read()
	{
		int z=0,f=1;
		char c=getchar();
		while(c<'0'||c>'9')
		{
			if(c=='-')
			{
				f=-1;
			}
			c=getchar();
		}
		while(c>='0'&&c<='9')
		{
			z=z*10+c-'0';
			c=getchar();
		}
		return z*f;
	}
}
using namespace fastio;
int n,siz[2000006],fa[2000006],id[2000006];
long long a[2000006];
bool vis[2000006];
unsigned long long ans,mn[2000006],mx[2000006];
bool cmp(int x,int y)
{
	return a[x]>a[y];
}
int find(int x)
{
	if(fa[x]==x)
	{
		return x;
	}
	return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
	int fx=find(x),fy=find(y);
	if(fx!=fy)
	{
		fa[fy]=fx;
		siz[fx]+=siz[fy];
		mn[fx]=min(mn[fx],mn[fy]);
		mx[fx]=max(mx[fx],mx[fy]);
		ans=max(ans,mx[fx]*mn[fx]*siz[fx]);
	}
}
signed main()
{
//	freopen("sketch.in", "r", stdin);
//	freopen("sketch.out", "w", stdout);
	n=read();
//	vector<int>fa(n+1),id(n+1);
//	vector<long long>a(n+1),siz(n+1);
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		mn[i]=mx[i]=a[i];
		siz[i]=1;
		ans=max(ans,a[i]*a[i]*1ull);
		id[i]=fa[i]=i;
	}
	sort(id+1,id+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		vis[id[i]]=1;
		if(vis[id[i]-1])
		{
			merge(id[i],id[i]-1);
		}
		if(vis[id[i]+1])
		{
			merge(id[i],id[i]+1);
		}
	}
	cout<<ans;
	return 0;
}

T2

题面:

一道很恶心的线段树题。

正解的大致意思就是用一颗线段树来维护已经固定了的点,然后看看每个区间的端点的奇偶性是否相同,如果相同,那就把中间的全填上相同奇偶性的值,如果不同, 那就必须多一个郁闷值了,因为不管你怎么填它都是一样的,所以这种区间就先跳过不填。

这里还要注意一下:根据贪心可以得到:当两端奇偶性相同时,应该从长度最短的区间开始填,直到填不了为止。

剩下的作者也不想写了,因为题目实在是太复杂,代码解释起来也很费力,如果可以,我以后单独开一篇文章讲一讲这题。

代码先放在这:

#include<bits/stdc++.h>
#define int long long
#define code using
#define by namespace
#define plh std
code by plh;
namespace fastio
{
	int read()
	{
		int z=0,f=1;
		char c=getchar();
		while(c<'0'||c>'9')
		{
			if(c=='-')
			{
				f=-1;
			}
			c=getchar();
		}
		while(c>='0'&&c<='9')
		{
			z=z*10+c-'0';
			c=getchar();
		}
		return z*f;
	}
	void write(int x,int k)
	{
		if(x<0)
		{
			putchar('-');
			x=-x;
		}
		if(x==0)
		{
			if(!k)
			{
				putchar('0');
			}
			return;
		}
		write(x/10,k+1);
		putchar(char(x%10+'0'));
	}
}
struct Tree{
	int sumlen[1000006],cntlen[1000006];
	int lowbit(int x)
	{
		return x&-x;
	}
	void add(int x)
	{
		for(int i=x;i<=1000000;i+=lowbit(i))
		{
			sumlen[i]+=x;
			cntlen[i]++;
		}
	}
	void del(int x)
	{
		for(int i=x;i<=1000000;i+=lowbit(i))
		{
			sumlen[i]-=x;
			cntlen[i]--;
		}
	}
	pair<int,int> qry(int x)
	{
		int ans=0,y=0;
		for(int i=20;i>=0;i--)
		{
			if(y+(1<<i)<=1000000&&x>=sumlen[y+(1<<i)])
			{
				y+=(1<<i);
				x-=sumlen[y];
				ans+=cntlen[y];
			}
		}
		ans+=x/(y+1);
		x%=(y+1);
		return {ans,x};
	}
}tr[2];
using namespace fastio;
int n,m,q,cnt1,cnt2,a[1000006],t[2];
set<int>s;
void query()
{
	if(s.size()==2)
	{
		if(t[0]>=n||t[1]>=n)
		{
			write(0,0);
			return;
		}
		write(1,0);
		return;
	}
	int ans=cnt1*2+cnt2;
	int st[5],top=0;
	if(a[1]==0)
	{
		auto it=++s.begin();
		int len=(*it)-1;
		tr[a[0]&1].del(len);
		st[++top]=(a[*it]&1);
		st[++top]=-len;
		ans--;
	} 
	if(a[n]==0) 
	{
		auto it=--s.end();
		it--;
		int len=n-(*it);
		tr[a[*it]&1].del(len);
		st[++top]=(a[*it]&1);
		st[++top]=len;
		ans--;
	}
	auto it0=tr[0].qry(t[0]),it1=tr[1].qry(t[1]);
	ans-=it0.first*2;
	ans-=it1.first*2;
	while(top)
	{
		bool fl=false;
		if(st[top]<0)
		{
			fl=true;
			st[top]=-st[top];
		}
		if(st[top-1])
		{
			if(it1.second>=st[top])
			{
				it1.second-=st[top];
				ans--;
			}
		}
		else
		{
			if(it0.second>=st[top])
			{
				it0.second-=st[top];
				ans--;
			}
		}
		if(!fl)
		{
			tr[st[top-1]].add(st[top]);
		}
		else
		{
			tr[0].add(st[top]);
		}
		top-=2;
	}
	write(ans,0);
}
void push(auto x,auto y)
{
	if((*x)+1<(*y))
	{
		if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1))
		{
			tr[a[*x]&1].add((*y)-(*x)-1);
			cnt1++;
		} 
		else
		{
			cnt2++;
		}
	}
	else
	{
		if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1))
		{
			return;
		}
		else
		{
			cnt2++;
		}
	}
}
void pop(auto x,auto y)
{
	if((*x)+1<(*y))
	{
		if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1))
		{
			tr[a[*x]&1].del((*y)-(*x)-1);
			cnt1--;
		} 
		else
		{
			cnt2--;
		}
	}
	else
	{
		if((*x)==0||(*y)==n+1||(a[*x]&1)==(a[*y]&1)) 
		{
			return;
		}
		else
		{
			cnt2--;
		}
	}
}
void delet(int x)
{
	auto it=s.lower_bound(x);
	auto itl=it,itr=it;
	itl--,itr++;
	pop(itl,it),pop(it,itr);
	a[x]=0;
	push(itl,itr);
	s.erase(it);
}
void adde(int x,int y)
{
	auto itr=s.lower_bound(x);
	auto itl=itr;
	itl--;
	pop(itl,itr);
	s.insert(x);
	a[x]=y;
	auto it=s.lower_bound(x);
	itl=it,itr=it;
	itl--,itr++;
	push(itl,it);
	push(it,itr);
}
signed main()
{
	cin>>n>>m>>q;
	s.insert(0),s.insert(n+1);
	for(int i=1;i<=n;i++)
	{
		t[read()&1]++;
	}
	for(int i=1,x,y;i<=m;i++)
	{
		x=read(),y=read();
		a[x]=y;
		s.insert(x);
		t[y&1]--;
	}
	for(auto i=++s.begin(),j=s.begin();i!=s.end();j=i,i++)
	{
		push(j,i);
	}
	while(q--)
	{
		int op=read();
		if(op==1)
		{
			int x=read();
			t[a[x]&1]++;
			delet(x);
		}
		else
		{
			int x=read(),y=read();
			adde(x,y);
			t[a[x]&1]--;
		}
		query();
		putchar('\n');
	}
	return 0;
}

T3

题面:

一道水到不能再水的题……(但我还是挂了……)

很简单:枚举一个点 iii 作为起点,然后按照 ddd 的距离跳,看看有多少个 111 在上面,那么其他的 111 就是要删的,然后暴力枚举一下得出答案即可。

时间复杂度:O(d×nd)=O(n)O(d\times\cfrac{n}{d})=O(n)O(d×dn)=O(n)

考场上写 freopen 不小心写错了,然后就 CE 了……(你问我为什么会 CE,我只能说我没编译……)

T4

题面:

题目很短,但很恶心,这里不多讲(因为这一道题用了七种算法,你让我怎么讲……),以后有时间单独开一篇文章讲一讲。

代码放在这展览一下:

#include<bits/stdc++.h>
#define int long long
#define code using
#define by namespace
#define plh std
code by plh;
const int LIM=100;
const int B=100;
int n,m;
namespace Tree
{
	vector<pair<int,int>>e[600006];
	void build(int x,int y,int z)
	{
		e[x].push_back({y,z});
		e[y].push_back({x,z});
	}
	int st[600006][26];//st表 
	int lg[600006];//log2函数 
	int siz[300006];//子树大小 
	int tdfn[300006];//欧拉序 
	int num=0,dfn[300006];//欧拉序+1 
	int depth[300006];//深度 
	int dis[300006];//到根节点的距离
	void dfs(int x,int fx)
	{
		dfn[x]=++num;
		depth[x]=depth[fx]+1;
		st[num][0]=x;
		siz[x]=1;
		tdfn[x]=++tdfn[0];
		for(auto i:e[x])
		{
			if(i.first!=fx)
			{
				dis[i.first]=dis[x]+i.second;
				dfs(i.first,x);
				st[++num][0]=x;
				siz[x]+=siz[i.first];
			}
		}
	}
	void st_()
	{
		for(int i=2;i<=(n<<1);i++)
		{
			lg[i]=lg[i>>1]+1;
		}
		dfs(1,0);
		for(int i=1;i<=20;i++)
		{
			for(int j=1;j+(1<<i)-1<=(n<<1);j++)
			{
				if(depth[st[j][i-1]]<depth[st[j+(1<<i-1)][i-1]])
				{
					st[j][i]=st[j][i-1];
				}
				else
				{
					st[j][i]=st[j+(1<<i-1)][i-1];
				}
			}
		}
	}
	int lca(int x,int y)
	{
		x=dfn[x],y=dfn[y];
		if(x>y)
		{
			swap(x,y);
		}
		int k=lg[y-x+1];
		return depth[st[x][k]]<depth[st[y-(1<<k)+1][k]]?st[x][k]:st[y-(1<<k)+1][k];
	}
	int dist(int x,int y)
	{
		return dis[x]+dis[y]-dis[lca(x,y)]*2;
	}
	int isfa(int x,int y)
	{
		return tdfn[x]<=tdfn[y]&&tdfn[x]+siz[x]-1>=tdfn[y];
	}
}
using Tree::dfn;
using Tree::lca;
using Tree::dist;
using Tree::dis;
using Tree::isfa;
namespace VirtualTree
{
	int fa[300006];//父亲节点
	int son[300006];//链最长的儿子节点 
	int len[300006];//底下的最长链 
	int w[300006];//每个点到父亲节点的边权
	vector<pair<int,int>>e[300006];//虚树 
	void dfs(int x,int fx)
	{
		fa[x]=fx;
		son[x]=0;
		for(auto i:e[x])
		{
			if(i.first==fx)
			{
				continue;
			}
			w[i.first]=i.second;
			dfs(i.first,x);
			if(len[i.first]+i.second>=len[son[x]]+w[son[x]])
			{
				son[x]=i.first;
			}
		}
		len[x]=(son[x]?w[son[x]]+len[son[x]]:0);
	} 
	vector<pair<int,int>> build(vector<int>&v)
	{
		sort(v.begin(),v.end(),[&](int a,int b)
		{
			return dfn[a]<dfn[b];
		});
		vector<int>vv(v.size()+v.size()-1);
		int cnt=0;
		for(int i=0;i<v.size()-1;i++)
		{
			vv[cnt++]=lca(v[i],v[i+1]);
		}
		for(auto i:v)
		{
			vv[cnt++]=i;
		}
		sort(vv.begin(),vv.end());
		vv.erase(unique(vv.begin(),vv.end()),vv.end());
		sort(vv.begin(),vv.end(),[&](int a,int b)
		{
			return dfn[a]<dfn[b];
		});
		for(auto i:vv)
		{
			e[i].clear();
		}
		static int top=1,stk[300006];
		stk[top]=vv[0];
		int rt=vv[0];
		for(int i=1;i<vv.size();i++)
		{
			int x=vv[i];
			while(top>1&&!isfa(stk[top],x))
			{
				e[stk[top]].push_back({stk[top-1],dis[stk[top]]-dis[stk[top-1]]});
				e[stk[top-1]].push_back({stk[top],dis[stk[top]]-dis[stk[top-1]]});
				top--;
			}
			stk[++top]=x;
		}
		while(top>1)
		{
			e[stk[top]].push_back({stk[top-1],dis[stk[top]]-dis[stk[top-1]]});
			e[stk[top-1]].push_back({stk[top],dis[stk[top]]-dis[stk[top-1]]});
			top--;
		}
		//下面是贪心
		static int dis[300006];
		static bool vis[300006];
		for(auto i:vv)
		{
			vis[i]=0;
		}
		queue<int>q;
		q.push(rt);
		dis[rt]=0;
		vis[rt]=1;
		while(!q.empty())
		{
			int x=q.front();
			q.pop();
			for(auto i:e[x])
			{
				int y=i.first;
				int z=i.second;
				if(vis[y])
				{
					continue;
				}
				vis[y]=1;
				dis[y]=dis[x]+z;
				q.push(y);
			}
		}
		rt=0;
		int mx=-10;
		for(auto i:v)
		{
			if(dis[i]>=mx)
			{
				mx=dis[i];
				rt=i;
			}
		}
		dfs(rt,0);
		w[rt]=0;
		sort(vv.begin(),vv.end(),[&](int x,int y)->bool
		{
			if(x==rt)
			{
				return 1;
			}
			return len[x]+w[x]>len[y]+w[y];
		});
		vector<pair<int,int>>ans;
		vector<int>tmp;
		ans.push_back({rt,0ll});
		for(auto i:vv)
		{
			vis[i]=0;
		}
		int s=0;
		for(auto i:vv)
		{
			if(ans.size()>=LIM)
			{
				break;
			}
			if(vis[i])
			{
				tmp.push_back(i);
				continue;
			}
			int u=i;
			s+=len[i]+w[i];
			vis[i]=1;
			while(son[u])
			{
				u=son[u];
				vis[u]=1;
			}
			ans.push_back({u,s});
		}
		cnt=0;
		while(ans.size()<v.size()&&ans.size()<LIM)
		{
			ans.push_back({tmp[cnt++],s});
		}
		return ans;
	}
}
vector<pair<int,int>> merge(vector<pair<int,int>>&x,vector<pair<int,int>>&y)
{
	vector<int>a(x.size()+y.size());
	for(int i=0;i<x.size();i++)
	{
		a[i]=x[i].first;
	}
	for(int i=0;i<y.size();i++)
	{
		a[i+x.size()]=y[i].first;
	}
	return VirtualTree::build(a);
}
vector<pair<int,int>>st[3006][26];
int lg[300006],a[300006];
int zb(int x)
{
	return (x-1)/B+1;
}
int query(int l,int r,int k)
{
	int ll=zb(l),rr=zb(r);
	static int stk[300006];
	int top=0;
	if(ll==rr)
	{
		for(int i=l;i<=r;i++)
		{
			stk[++top]=a[i];
		}
	}
	else
	{
		for(int i=l;i<=ll*B;i++)
		{
			stk[++top]=a[i];
		}
		for(int i=(rr-1)*B+1;i<=r;i++)
		{
			stk[++top]=a[i];
		}
		l=ll+1,r=rr-1;
		if(l<=r)
		{
			int k=lg[r-l+1];
			vector<pair<int,int>>lv=st[l][k];
			vector<pair<int,int>>rv=st[r-(1<<k)+1][k];
			for(auto i:lv)
			{
				stk[++top]=i.first;
			}
			for(auto i:rv)
			{
				stk[++top]=i.first;
			}
		}
	}
	vector<int>v(stk+1,stk+1+top);
	vector<pair<int,int>>ans=VirtualTree::build(v);
	return ans[k-1].second;
}
void decode(int &l,int &r,int &k,int lstans,int testop)
{
    lstans%=19260817;
    if(testop)
	{
        l^=lstans;
		l=(l%n+n)%n+1;
        r^=lstans;
		r=(r%n+n)%n+1;
        if(l>r)
		{
			swap(l, r);
		}
        k^=lstans; 
        k=(k%min(r-l+1,100ll))+1;
    }
}
int op;
signed main()
{
	int c;
	cin>>c;
	cin>>op>>n;
	for(int i=1,x,y,z;i<n;i++)
	{
		cin>>x>>y>>z;
		Tree::build(x,y,z);
	}
	Tree::st_();
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(int i=2;i<=n;i++)
	{
		lg[i]=lg[i>>1]+1;
	}
	int m=zb(n);
	for(int i=1;i<=n;i+=B)
	{
		int l=i,r=min(i+B-1,n);
		vector<int>vec(a+l,a+r+1);
		st[zb(i)][0]=VirtualTree::build(vec); 
	}
	for(int i=1;i<=20;i++)
	{
		for(int j=1;j+(1<<i)<=m;j++)
		{
			st[j][i]=merge(st[j][i-1],st[j+(1<<i-1)][i-1]);
		}
	}
	int q;
	cin>>q;
	int lstans=0;
	while(q--)
	{
		int l,r,k;
		cin>>l>>r>>k;
		decode(l,r,k,lstans,op);
		lstans=query(l,r,k);
		cout<<lstans<<endl;
	}
	return 0;
}

其他的话

你可能觉得这篇文章好像就这样被我水过去了(实际上确实如此),但我是真的不想再提及这件事,因此每道题写的比较简短,我也没想过这篇文章的质量分能有多高。(而且,水的题不也很水吗?难的题不也很难吗?)

还有就是上面我的所有承诺必定会实现,请各位读者帮忙监督我(不然我怕我会忘)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值