测试「2020牛客NOIP赛前集训营-提高组(第三场)总结」

T1

(A+B) 看作一个整体,记作 (T) 。则:

第一种变化:

[egin{aligned} T & o 2 imes T\ C & o C-T end{aligned} ]

第二种变化:

[egin{aligned} T & o T-C\ C & o 2 imes C end{aligned} ]

发现 (T+C) 始终不变,令 (C+T=S) ,则 (T=S-C)

发现第一种变化中 (C o C-T=C-(S-C)=2 imes C-S=2 imes C { m mod} S) ,第二种变化中 (C o 2 imes C=2 imes C { m mod} S)

那么得出结论:(K) 轮变化后,(C o C imes 2^K { m mod} S) ,快速幂即可。

( ext{Code}:)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long lxl;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

inline lxl fmi(lxl a,lxl b,lxl mod)
{
	lxl res=1;
	a%=mod;
	while(b>0)
	{
		if(b&1) (res*=a)%=mod;
		(a*=a)%=mod;
		b>>=1;
	}
	return res;
}

lxl A,B,C,K;

int main()
{
#ifndef ONLINE_JUDGE
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
#endif
	int T;read(T);
	while(T--)
	{
		read(A),read(B),read(C),read(K);
		lxl mod=A+B+C;
		printf("%lld
",C*fmi(2,K,mod)%mod);
	}
	return 0;
}

T2

考虑从小到大加边,记录一个前缀和即可。注意判断一条边都不能走的情况的种类数是 (1)

( ext{Code}:)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <bitset>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=5e5+5,maxc=605;
int mod;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int u,v;
	lxl w;
	edge(int u,int v,lxl w):u(u),v(v),w(w){}
	edge(){}
	inline bool operator < (const edge &T)const
	{
		return w<T.w;
	}
}e[maxn];

int n,m,q,start,opt;
int color[maxn];
int fa[maxn];
bitset<maxc> siz[maxn];
lxl sum[maxn],f[maxn];

inline int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}

inline bool merge(int u,int v)
{
	int x=find(u),y=find(v);
	if(x==y) return false;
	fa[x]=y;
	siz[y]=siz[x]|siz[y];
	return true;
}

inline lxl query(lxl x)
{
	int pos=upper_bound(e+1,e+m+1,edge(-1,-1,x))-e-1;
	return sum[pos]+(x-e[pos].w+1)*f[pos];
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
#endif
	read(n),read(m),read(q),read(start),read(opt);
	if(opt) read(mod);
	for(int i=1;i<=n;++i) read(color[i]);
	for(int i=1,u,v,w;i<=m;++i)
	{
		read(u),read(v),read(w);
		e[i]=edge(u,v,w);
	}
	for(int i=1;i<=n;++i)
	{
		fa[i]=i;
		siz[i].set(color[i]);
	}
	sort(e+1,e+m+1);
	for(int i=1;i<=m;++i)
	{
		int u=e[i].u,v=e[i].v;
		merge(u,v);
		f[i]=siz[find(start)].count();
	}
	f[0]=1;
	for(int i=1;i<=m;++i)
		sum[i]=sum[i-1]+f[i-1]*(e[i].w-e[i-1].w);
	int l,r;
	lxl lastans=0;
	while(q--)
	{
		read(l),read(r);
		if(opt) l=(l^lastans)%mod+1,r=(r^lastans)%mod+1;
		if(l>r) swap(l,r);
		lastans=query(r)-query(l-1);
		printf("%lld
",lastans);
	}
	return 0;
}

T3

令某个进行过1操作的结点编号为 (i) ,操作的时刻为 (t_i) ,令询问的点为 (x) ,当前时刻为 (t_x) ,也就是说要求一个点 (i),满足:

[dis(i,x)leq t_x-t_i ]

(LCA(i,x)=y) 拆一下式子:

[egin{aligned} dep_i+dep_x-2 imes dep_y&leq t_x-t_i\ t_i+dep_i-2 imes dep_y&leq t_x-dep_x end{aligned} ]

这个式子的右边只和 (x) 有关,右边除了 (y) 只和 (i) 有关。尝试让左边也之和 (i) 有关。

(f)(i)(x) 的某个公共祖先,显然,当 (f=LCA(i,x)) ,即 (dep_f) 取到最大值时,下面这个式子的左边取到最小值:

[t_i+dep_i-2 imes dep_fleq t_x-dep_x ]

于是可以用线段树+树链剖分维护链上 (t_i+dep_i) 的最小值,和 (dep_f) 的最大值,进而得到区间内 (t_i+dep_i-2 imes dep_f) 的最小值。

进行操作1时,更新 (x) 到根的路径上的 (t_i+dep_i)

查询时,只需要查询 (x) 到根的路径上 (t_i+dep_i-2 imes dep_f) 的最小值,判断它与 (t_x-dep_x) 的大小关系即可。

( ext{Code}:)

#include <iostream>
#include <cstring>
#include <cstdio>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=1e5+5;

#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int u,v,next;
	edge(int u,int v,int next):u(u),v(v),next(next){}
	edge(){}
}e[maxn<<1];

int head[maxn],ecnt;

inline void add(int u,int v)
{
	e[ecnt]=edge(u,v,head[u]);
	head[u]=ecnt++;
}

int n,m;

int dfn[maxn],idx[maxn],dep[maxn],dfs_cnt;
int fa[maxn],top[maxn],son[maxn],siz[maxn];

void dfs1(int u)
{
	dep[u]=dep[fa[u]]+1;
	siz[u]=1;
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa[u]) continue;
		fa[v]=u;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[son[u]]<siz[v]) son[u]=v;
	}
}

void dfs2(int u,int t)
{
	dfn[u]=++dfs_cnt;
	idx[dfn[u]]=u;
	top[u]=t;
	if(!son[u]) return;
	dfs2(son[u],t);
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}

namespace Segment_Tree
{
	struct node
	{
		int l,r;
		int Maxdis,Ans,lazy;
		bool clear;
		node(int l,int r,int Maxdis=0,int Ans=INF,int lazy=INF,bool clear=false)
			:l(l),r(r),Maxdis(Maxdis),Ans(Ans),lazy(lazy),clear(clear){}
		node(){}
	}tree[maxn<<2];
	#define ls (p<<1)
	#define rs (p<<1|1)
	inline void clear(int p)
	{
		tree[p].Ans=tree[p].lazy=INF;
		tree[p].clear=true;
	}
	inline void push_down(int p)
	{
		if(!tree[p].clear) return;
		clear(ls);clear(rs);
		tree[p].clear=false;
	}
	inline void update(int p)
	{
		tree[p].Ans=min(tree[p].lazy-tree[p].Maxdis,min(tree[ls].Ans,tree[rs].Ans));
	}
	void build(int p,int l,int r)
	{
		tree[p]=node(l,r);
		if(l==r) return tree[p].Maxdis=2*dep[idx[l]],void();
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		tree[p].Maxdis=max(tree[ls].Maxdis,tree[rs].Maxdis);
	}
	inline void calcu(int p,int d)
	{
		tree[p].lazy=min(tree[p].lazy,d);
		tree[p].Ans=min(tree[p].Ans,tree[p].lazy-tree[p].Maxdis);
	}
	void modify(int p,int L,int R,int d)
	{
		int l=tree[p].l,r=tree[p].r;
		if(L<=l&&r<=R) return calcu(p,d),void();
		push_down(p);
		int mid=(l+r)>>1;
		if(L<=mid) modify(ls,L,R,d);
		if(R>mid) modify(rs,L,R,d);
		update(p);
	}
	int query(int p,int L,int R,int val=INF)
	{
		int l=tree[p].l,r=tree[p].r;
		if(L<=l&&r<=R) return min(tree[p].Ans,val-tree[p].Maxdis);
		push_down(p);
		int mid=(l+r)>>1,ans=INF;
		if(L<=mid) ans=min(ans,query(ls,L,R,min(val,tree[p].lazy)));
		if(R>mid) ans=min(ans,query(rs,L,R,min(val,tree[p].lazy)));
		return ans;
	}
}

inline void modify(int x,int d)
{
	for(;x;x=fa[top[x]])
		Segment_Tree::modify(1,dfn[top[x]],dfn[x],d);
}

inline int query(int x)
{
	int res=INF;
	for(;x;x=fa[top[x]])
		res=min(res,Segment_Tree::query(1,dfn[top[x]],dfn[x]));
	return res;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
#endif
	read(n),read(m);
	memset(head,-1,sizeof(head));
	for(int i=1,u,v;i<n;++i)
	{
		read(u),read(v);
		add(u,v);add(v,u);
	}
	dfs1(1);
	dfs2(1,1);
	Segment_Tree::build(1,1,n);
	int opt,x;
	for(int i=1;i<=m;++i)
	{
		read(opt),read(x);
		if(opt==1) modify(x,i+dep[x]);
		else if(opt==2) Segment_Tree::clear(1);
		else puts(query(x)<=i-dep[x]?"wrxcsd":"orzFsYo");
	}
	return 0;
}

T4

因为攻击力始终不变,所以每个魔物的攻击次数是固定的,记为 (H_i) ,将打败这个魔物获得的宝石数记为 (B_i) ,则打败这个魔物后,打之后的每个魔物 (j) 受到的伤害均会减少 (H_j imes B_i)

考虑一个最优的打怪序列:({p_1,p_2,cdots,p_{n-1}}) 。若交换其中的 (p_i)(p_{i+1}) ,那么减少的总伤害将会变化 (H_i imes B_{i+1}-H_{i+1} imes B_i) ,因为交换前的序列一定更优,则有:

[egin{aligned} H_i imes B_{i+1}-H_{i+1} imes B_ileq 0\ frac{B_i}{H_i}geq frac{B_{i+1}}{H_{i+1}} end{aligned} ]

于是按照 (frac{B_i}{H_i}) 排序,依次打。

若当前性价比最高的魔物还打不到,那么打完它的父亲后一定会立刻打它,于是在堆贪心时用并查集缩点即可。

( ext{Code}:)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
const int maxn=1e5+5;

template <typename T>
inline void read(T &x)
{
	x=0;T f=1;char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	x*=f;
}

struct edge
{
	int u,v,next;
	edge(int u,int v,int next):u(u),v(v),next(next){}
	edge(){}
}e[maxn<<1];

int head[maxn],ecnt;

inline void add(int u,int v)
{
	e[ecnt]=edge(u,v,head[u]);
	head[u]=ecnt++;
}

namespace Union_Find
{
	int fa[maxn];
	inline int find(int x) {return fa[x]==x?x:fa[x]=find(fa[x]);}
}

int n;
typedef pair<double,int> pii;
priority_queue<pii> q;

struct creature
{
	lxl lif,att,def;
}cre[maxn];

lxl lif,att,def;
lxl H[maxn],B[maxn];
lxl H1[maxn],B1[maxn];
int fa[maxn];

void dfs(int u)
{
	for(int i=head[u];~i;i=e[i].next)
	{
		int v=e[i].v;
		if(v==fa[u]) continue;
		fa[v]=u;
		dfs(v);
	}
}

bool tag[maxn],vis[maxn];
vector<int> siz[maxn];

void calcu(int u)
{
	tag[u]=true;
	lif-=(cre[u].att-def)*H[u];
	def+=B[u];
	for(auto v:siz[u])
		calcu(v);
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("D.in","r",stdin);
#endif
	read(n);
	memset(head,-1,sizeof(head));
	for(int i=1,u,v;i<n;++i)
	{
		read(u),read(v);
		add(u,v);add(v,u);
	}
	dfs(1);
	read(lif),read(att),read(def);
	for(int i=2;i<=n;++i)
	{
		read(cre[i].lif);
		read(cre[i].att);
		read(cre[i].def);
		read(B[i]);
		H[i]=(cre[i].lif-1)/(att-cre[i].def);
		B1[i]=B[i];H1[i]=H[i];
		q.push(make_pair((double)B[i]/H[i],i));
	}
	tag[1]=true;
	for(int i=1;i<=n;++i)
		Union_Find::fa[i]=i;
	while(!q.empty())
	{
		int u=q.top().second;q.pop();
		if(vis[u]) continue;
		vis[u]=true;
		if(tag[fa[u]]) {calcu(u);continue;}
		int x=Union_Find::find(fa[u]);
		H1[x]+=H1[u],B1[x]+=B1[u];
		siz[x].push_back(u);
		Union_Find::fa[u]=x;
		q.push(make_pair((double)B1[x]/H1[x],x));
	}
	printf("%lld
",lif);
	return 0;
}
原文地址:https://www.cnblogs.com/syc233/p/13867741.html