Loading

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

T1

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

第一种变化:

\[\begin{aligned} T &\to 2\times T\\ C &\to C-T \end{aligned} \]

第二种变化:

\[\begin{aligned} T &\to T-C\\ C &\to 2\times C \end{aligned} \]

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

发现第一种变化中 \(C\to C-T=C-(S-C)=2\times C-S=2\times C\ {\rm mod} \ S\) ,第二种变化中 \(C\to 2\times C=2\times C\ {\rm mod} \ S\)

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

\(\text{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\n",C*fmi(2,K,mod)%mod);
	}
	return 0;
}

T2

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

\(\text{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\n",lastans);
	}
	return 0;
}

T3

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

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

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

\[\begin{aligned} dep_i+dep_x-2\times dep_y&\leq t_x-t_i\\ t_i+dep_i-2\times 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\times dep_f\leq t_x-dep_x \]

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

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

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

\(\text{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\times B_i\)

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

\[\begin{aligned} H_i\times B_{i+1}-H_{i+1}\times B_i\leq 0\\ \frac{B_i}{H_i}\geq \frac{B_{i+1}}{H_{i+1}} \end{aligned} \]

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

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

\(\text{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\n",lif);
	return 0;
}
posted @ 2020-10-24 10:12  GoPoux  阅读(127)  评论(0编辑  收藏  举报