noip模拟27[妹子图·腿·腰](fengwu半仙的妹子们)

\(noip模拟27 solutions\)

这次吧,我本来以为我能切掉两个题,结果呢??只切掉了一个

不过,隔壁Varuxn也以为能切两个,可惜了,他一个都没切。。。。。。

确实他分比我高一点,但是吧,这个人就改题非常慢,所以结论就是

我牛逼,牛逼到家了

所以我应该是挂掉了100pts,下次注意,下次AK

\(T1 妹子图(graph)\)

这个题有一堆做法,而且旗鼓相当,复杂度都一样,跑的也差不多快

我的做法是在无向图上跑魔改拓扑排序+魔改DIJ,非常的恶心,但是吧,理解之后极其简单

按照堆优化的DIJ搞了一个优先队列,这样我们先把半仙的家能够连到的地方压到队列中

因为我们是按照边权小的在前面,所以我们当前找到的一定是当前困难抵抗值能够到达的最大的图

显然有一些困难抵抗值能够见到的妹子种类数是一样的,这些我们就不必保存了

这样的话,我们的数组里最多只有2e5个点,直接保证了时间和空间复杂度

我们开始弹队列,然后更新这个数组,这里面存的东西就是

能够看到当前这么多妹子的最小困难抵抗值,和能够看到的妹子

最后的时候直接在这个数组里二分查找,搞一搞边界就好了

复杂度是\(O(nlogn+qlogn)\)

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=5e5+5;
int n,m,qq,hom,opt,mod;
int c[N];
ll num[N],fro[N],bas[N],cnt;
int to[N*2],nxt[N*2],val[N*2],head[N],rp;
void add_edg(int x,int y,int z){
	to[++rp]=y;
	val[rp]=z;
	nxt[rp]=head[x];
	head[x]=rp;
}
int ton[N],sum;
bool vis[N];
struct node{
	int val,tag;
	node(){}
	node(int x,int y){
		val=x;tag=y;
	}
	bool operator < (node x)const{
		return val>x.val;
	}
};
priority_queue<node> q;
signed main(){
	scanf("%d%d%d%d%d",&n,&m,&qq,&hom,&opt);
	if(opt)scanf("%d",&mod);
	for(re i=1;i<=n;i++){
		scanf("%d",&c[i]);
	}
	for(re i=1,x,y,z;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add_edg(x,y,z);
		add_edg(y,x,z);
	}
	ton[c[hom]]++;sum++;
	for(re i=head[hom];i;i=nxt[i])
		q.push(node(val[i],to[i]));
	int cge=0;bas[++cnt]=1;num[cnt]=sum;
	while(!q.empty()){
		int dis=q.top().val;
		int now=q.top().tag;
		q.pop();
		if(vis[now])continue;
		vis[now]=true;
		cge=max(cge,dis);
		ton[c[now]]++;
		if(ton[c[now]]==1){
			sum++;
			if(bas[cnt]!=cge)
				bas[++cnt]=cge;
			num[cnt]=sum;
		}
		for(re i=head[now];i;i=nxt[i]){
			q.push(node(val[i],to[i]));
		}
	}
	bas[++cnt]=1e9+1;
	for(re i=1;i<=cnt;i++){
		fro[i]=fro[i-1]+1ll*num[i-1]*(bas[i]-bas[i-1]);
		//cout<<bas[i]<<" "<<num[i]<<" "<<fro[i]<<endl;
	}
	ll lans=0;
	for(re i=1;i<=qq;i++){
		ll x,y;
		scanf("%lld%lld",&x,&y);
		if(opt==1){
			x=(x^lans)%mod+1;
			y=(y^lans)%mod+1;
		}
		if(x>y)swap(x,y);
		int ld=upper_bound(bas+1,bas+cnt+1,x)-bas;
		int rd=upper_bound(bas+1,bas+cnt+1,y)-bas-1;
		//cout<<ld<<" "<<rd<<" "<<num[ld]<<" "<<bas[ld]<<endl;
		ll ans=fro[rd]-fro[ld];
		ans+=1ll*(bas[ld]-x)*num[ld-1];
		ans+=1ll*(y-bas[rd]+1)*num[rd];
		printf("%lld\n",ans);
		lans=ans;
	}
}

\(T2\;妹子腿(tree)\)

真是没想到啊,没想到,这个竟然是个大暴力,主要是他还能过,气死人,比线段树还快

考场上没敢写,气人。

就是维护被标记的点的标记时间以及和要查询的点的距离,

这个距离就是树剖+LCA,完事!!!

注意优化,要不然过不了,如果要标记的点已经在不断的扩散中被标记了,就不要加进去了。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define fi first
#define se second
#define pa pair<int,int>
#define mpa(x,y) make_pair(x,y)
const int N=1e5+5;
int n,m;
int to[N*2],nxt[N*2],head[N],rp;
void add_edg(int x,int y){
	to[++rp]=y;
	nxt[rp]=head[x];
	head[x]=rp;
}
int fa[N],dep[N];
int siz[N],son[N],top[N];
void dfs1(int x){
	siz[x]=1;son[x]=0;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa[x])continue;
		fa[y]=x;dep[y]=dep[x]+1;
		dfs1(y);
		siz[x]+=siz[y];
		if(!son[x]||siz[y]>siz[son[x]])son[x]=y;
	}
}
void dfs2(int x,int f){
	top[x]=f;
	if(son[x])dfs2(son[x],f);
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==fa[x]||y==son[x])continue;
		dfs2(y,y);
	}
}
int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
int DIS(int x,int y){
	return dep[x]+dep[y]-2*dep[LCA(x,y)];
}
pa ji[N];
int cnt;
signed main(){
	scanf("%d%d",&n,&m);
	for(re i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		add_edg(x,y);add_edg(y,x);
	}
	dfs1(1);dfs2(1,1);
	for(re i=1,typ,x;i<=m;i++){
		scanf("%d%d",&typ,&x);
		if(typ==1){
			int flag=0;
			for(re j=1;j<=cnt;j++){
				if(DIS(ji[j].fi,x)<=i-ji[j].se){
					flag=1;break;
				}
			}
			if(flag==1)continue;
			ji[++cnt]=mpa(x,i);
		}
		if(typ==2)cnt=0;
		if(typ==3){
			int flag=0;
			for(re j=1;j<=cnt;j++){
				if(DIS(ji[j].fi,x)<=i-ji[j].se){
					flag=1;break;
				}
			}
			if(flag==1)printf("wrxcsd\n");
			else printf("orzFsYo\n");
		}
	}
}

\(T3\;妹子腰(序列)\)

这个题我一眼就是原题,而且是昨天的原题

后来发现不对,是极长上升而不是最长上升

所以我忘记了如何维护这个东西了,考完试又去回顾了一下,好难哟!!!

就是一个\(O(log^2n)\)的线段树,注意维护一个底线,来保证是极长的。。

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=2e5+5;
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll mod=998244353;
int n,a[N],maxd;
struct node{
	#define ls x<<1
	#define rs x<<1|1
	ll sum[N*4];
	int mxra[N*4];
	ll work(int x,int l,int r,int maxn){
		//cout<<x<<" "<<l<<" "<<r<<endl;
		if(l==r){
			if(mxra[x]>maxn)return sum[x];
			return 0;
		}
		int mid=l+r>>1;
		if(maxn>mxra[x])return 0;
		else if(maxn>mxra[rs])return work(ls,l,mid,maxn);
		else{
			ll ret=0;
			ret=(ret+work(rs,mid+1,r,maxn))%mod;
			ret=(ret+work(ls,l,mid,mxra[rs]))%mod;
			return ret;
		}
	}
	void pushup(int x){
		mxra[x]=max(mxra[ls],mxra[rs]);
		//sum[x]=(sum[ls]+sum[rs])%mod;
		return ;
	}
	void ins(int x,int l,int r,int pos,int mx,int v){
		if(l==r){
			mxra[x]=mx;
			sum[x]=v;
			return ;
		}
		int mid=l+r>>1;
		if(pos<=mid)ins(ls,l,mid,pos,mx,v);
		else ins(rs,mid+1,r,pos,mx,v);
		pushup(x);return ;
	}
	ll query(int x,int l,int r,int pos){
		if(pos==0)return 0;
		if(r<=pos){
			ll ret=work(x,l,r,maxd);
			maxd=max(maxd,mxra[x]);
			return ret;
		}
		int mid=l+r>>1;ll ret=0;
		if(pos>mid)ret=(ret+query(rs,mid+1,r,pos))%mod;
		ret=(ret+query(ls,l,mid,pos))%mod;
		return ret;
	}
	#undef ls
	#undef rs
}xds;
ll ans,dp[N];
signed main(){
	scanf("%d",&n);
	for(re i=1;i<=n;i++)scanf("%d",&a[i]);
	for(re i=1;i<=n;i++){
		maxd=0;
		dp[i]=xds.query(1,1,n,a[i]-1);
		//cout<<dp[i]<<" ";
		if(dp[i]==0)dp[i]=1;
		xds.ins(1,1,n,a[i],i,dp[i]);
		//cout<<dp[i]<<endl;
	}
	int mx=0;
	for(re i=n;i>=1;i--){
		if(a[i]>mx)ans=(ans+dp[i])%mod,mx=a[i];
	}
	printf("%lld",ans);
}
posted @ 2021-07-29 16:19  fengwu2005  阅读(130)  评论(0编辑  收藏  举报