CWOI T1T2 训练

感觉难度还好?

A - Intercity Travelling

萌萌柿子题。\(f_i\) 表示前 \(i\) 个,\(i\) 是最后一段结尾的答案和,\(g_i\) 表示方案数。令 \(s_i\) 表示 \(a\) 的前缀和,显然有 \(f_i=\sum f_j+g_j\cdot s_{i-j}\)\(g_i=\sum g_j\),然后有 \(g_i=2^{\max(0,i-1)}\),然后对 \(j=0\) 单独看,剩下的推一推,前缀和一下就做完了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,mod=998244353,i2=(mod+1)/2;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,a[1000005],pw[1000005],ipw[1000005],s[1000005],f[1000005],g[1000005];
signed main(){
	n=read();f[0]=0,g[0]=0;
	pw[0]=1;for(int i=1;i<=n;i++)pw[i]=pw[i-1]*2%mod;
	ipw[0]=1;for(int i=1;i<=n;i++)ipw[i]=ipw[i-1]*i2%mod;
	for(int i=1;i<=n;i++)a[i]=read(),s[i]=(s[i-1]+a[i]*ipw[i]%mod)%mod;
	for(int i=1;i<=n;i++)f[i]=(f[i]+g[i-1]+pw[i]*s[i]%mod)%mod,g[i]=(g[i-1]+f[i])%mod;
	printf("%lld\n",f[n]);
	return 0;
}

B - Building Bridges

李超板子捏。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define db double
using namespace std;
const int inf=1e18,eps=0,SIZ=1e6;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct seg{
	int k,b;
	int calc(int x){
		return k*x+b;
	}
}s[100005];
db cross(int i,int j){
	return 1.0*(s[j].b-s[i].b)/(s[i].k-s[j].k);
}
int chmin(int i,int j,int x){
	int vi=s[i].calc(x),vj=s[j].calc(x);
	if(abs(vi-vj)>eps)return ((vi<vj)?i:j);
	return min(i,j);
}
struct segtree{
	#define ls p<<1
	#define rs p<<1|1
	#define lson l,mid,ls
	#define rson mid+1,r,rs
	struct Node{
		int flag,id;
		Node():flag(0),id(0){}
	}c[4000015];
	void update(int l,int r,int p,int id){
		if(c[p].flag==0){c[p].flag=1;c[p].id=id;return;}
		int& ID=c[p].id;
		if(l==r){
			if(s[ID].calc(l)>s[id].calc(l))ID=id;
			return;
		}
		int l1=s[ID].calc(l),r1=s[ID].calc(r);
		int l2=s[id].calc(l),r2=s[id].calc(r);
		if(l1<=l2&&r1<=r2)return;
		if(l1>l2&&r1>r2){ID=id;return;}
		int mid=(l+r)>>1;db pos=cross(ID,id);
		if(pos<=mid){
			if(l1<l2)swap(ID,id);
			update(lson,id);
		}
		else{
			if(r1<r2)swap(ID,id);
			update(rson,id);
		}
	}
	void insert(int l,int r,int p,int L,int R,int id){
		if(L<=l&&r<=R){
			update(l,r,p,id);
			return;
		}
		int mid=(l+r)>>1;
		if(L<=mid)insert(lson,L,R,id);
		if(R>mid)insert(rson,L,R,id); 
	}
	int query(int l,int r,int p,int x){
		if(l==r)return c[p].id;
		int mid=(l+r)>>1,id;int ID=c[p].id;
		if(x<=mid)id=query(lson,x);
		else id=query(rson,x);
		return chmin(ID,id,x);
	}
	#undef ls
	#undef rs
	#undef lson
	#undef rson
}Tr;
int f[100005],h[100005],w[100005],sum[100005];
void insert(int id){
	s[id]=(seg){-2*h[id],f[id]-sum[id]+h[id]*h[id]};
	Tr.insert(0,SIZ,1,0,SIZ,id);
}
int query(int id){
	int res=Tr.query(0,SIZ,1,h[id]);
	return s[res].k*h[id]+s[res].b+sum[id-1]+h[id]*h[id];
}
signed main(){
	int n=read();s[0]=(seg){0,inf};
	for(int i=1;i<=n;i++)h[i]=read();
	for(int i=1;i<=n;i++)w[i]=read(),sum[i]=sum[i-1]+w[i];
	f[1]=0;insert(1);
	for(int i=2;i<=n;i++)f[i]=query(i),insert(i);
	printf("%lld\n",f[n]);
	return 0;
}

C - Hotel加强版

长剖优化 dp。

考虑一个朴素的 \(\mathcal{O}(n^2)\) dp。\(f_{i,j}\) 表示 \(i\) 子树内与 \(i\) 距离为 \(j\) 的点数,\(g_{i,j}\) 表示 \(i\) 子树内满足 \(\text{dist}(x,i)=\text{dist}(y,i)\land \text{dist}(i,x)=\text{dist}(i,y)=2\text{dist}(x,y)-j\) 的无序对 \((x,y)\) 的数量。转移就是

\[f_{i,j}=\sum_{x\in\text{son}(i)}f_{x,j-1} \]

\[g_{i,j}=\sum_{x\in\text{son}(i)}g_{x,j+1}+\sum_{x,y\in\text{son}(i),x\ne y}f_{x,j-1}\times f_{y,j-1} \]

\[ans=\sum\limits_{i=1}^n g_{i,0}+\sum\limits_{i=1}^n\sum_{x,y\in\text{son}(i),x\ne y}f_{x,j-1}\times g_{y,j+1} \]

然后你发现 dp 的第二维是深度,可以长剖优化成 \(\mathcal{O}(n)\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[200005];
int tot,head[100005];
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int son[100005],len[100005];
void dfs1(int u,int fa){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs1(v,u);if(len[son[u]]<len[v])son[u]=v;
	}
	len[u]=len[son[u]]+1;
}
int n,ans,*f[100005],*g[100005],BUFF[700005],*I=BUFF;
void dfs2(int u,int fa){
	if(son[u])f[son[u]]=f[u]+1,g[son[u]]=g[u]-1,dfs2(son[u],u);
	f[u][0]++,ans+=g[u][0];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa||v==son[u])continue;
		f[v]=I,I+=(len[v]<<1)+1,g[v]=I,I+=(len[v]<<1)+1;
		dfs2(v,u);
		for(int j=0;j<=len[v];j++){
			if(j-1>=0)ans+=f[u][j-1]*g[v][j];
			if(j+1<=len[u])ans+=g[u][j+1]*f[v][j];
		}
		for(int j=0;j<=len[v];j++){
			if(j+1<=len[u])g[u][j+1]+=f[u][j+1]*f[v][j];
			if(j-1>=0)g[u][j-1]+=g[v][j];
			if(j+1<=len[u])f[u][j+1]+=f[v][j];
		}
	}
}
signed main(){
	n=read();for(int i=1,u,v;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
	dfs1(1,0);f[1]=I,I+=(len[1]<<1)+1,g[1]=I,I+=(len[1]<<1)+1;dfs2(1,0);
	printf("%lld\n",ans);
	return 0;
}

D - 萌萌哒

只能说最后一步没想到/kk

我们开 \(\log n\) 个并查集,\(f_{i,j}\) 维护 \([j,j+2^i)\) 的信息。然后每个限制可以像 ST 表一样拆成两个区间,然后并查集合并即可。最后相当于求 \(f_{0,i}\) 有多少种。怎么做捏?

你发现直接从大到小枚举 \(i\),令 \(k=f_{i,j}\)。因为 \([j,j+2^i)\)\([k,k+2^i)\) 是一样的,所以 \([j,j+2^{i-1})\)\([k,k+2^{i-1})\)\([j+2^{i-1},j+2^i)\)\([k+2^{i-1},k+2^i)\) 也是一样的,传递下去即可。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,mod=1e9+7;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int Log[100005],fa[20][100005];
int find(int o,int x){
	return ((x==fa[o][x])?x:fa[o][x]=find(o,fa[o][x]));
}
void merge(int o,int x,int y){
	if((x=find(o,x))!=(y=find(o,y)))fa[o][x]=y;
}
signed main(){
	int n=read(),m=read(),ans=9,cnt=0;
	Log[1]=0;for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
	for(int i=0;i<=Log[n];i++)for(int j=1;j+(1ll<<i)-1<=n;j++)fa[i][j]=j;
	for(int i=1;i<=m;i++){
		int l1=read(),r1=read(),l2=read(),r2=read(),o=Log[r1-l1+1];
		merge(o,l1,l2),merge(o,r1-(1ll<<o)+1,r2-(1ll<<o)+1);
	} 
	for(int i=Log[n];i>=1;i--){
		for(int j=1;j+(1ll<<i)-1<=n;j++){
			int k=find(i,j);if(j==k)continue;
			merge(i-1,j,k),merge(i-1,j+(1ll<<(i-1)),k+(1ll<<(i-1)));
		}
	}
	for(int i=1;i<=n;i++)if(find(0,i)==i)cnt++;
	for(int i=1;i<cnt;i++)ans=ans*10%mod;
	printf("%lld\n",ans);
	return 0;
}

E - Lomsat gelral

可以 dsu on tree,也可以线段树合并/莫队。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define mk make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int,int>pii;
const int inf=1e18;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
struct edge{
	int v,nxt;
}e[200005];
int head[100005],tot;
void add(int u,int v){
	e[++tot]=(edge){v,head[u]},head[u]=tot;
}
int T,siz[100005],son[100005],dfn[100005],rnk[100005],c[100005];
void dfs1(int u,int fa){
	siz[u]=1,dfn[u]=++T,rnk[T]=u;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		dfs1(v,u);siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
int num[100005],id[100005];multiset<int>s;
void add(int u){
	s.erase(s.find(num[c[u]]));
	id[num[c[u]]]-=c[u];
	num[c[u]]++;
	s.insert(num[c[u]]);
	id[num[c[u]]]+=c[u];
}
void del(int u){
	s.erase(s.find(num[c[u]]));
	id[num[c[u]]]-=c[u];
	num[c[u]]--;
	s.insert(num[c[u]]);
	id[num[c[u]]]+=c[u];	
}
void calc(int u,int k){
	for(int i=dfn[u];i<=dfn[u]+siz[u]-1;i++){
		if(k==1)add(rnk[i]);
		else del(rnk[i]);
	}
}
int ans[100005];
void dfs2(int u,int fa,int tag){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		if(v!=son[u])dfs2(v,u,0);
	}
	if(son[u])dfs2(son[u],u,1);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;if(v==fa)continue;
		if(v!=son[u])calc(v,1);
	}
	add(u);ans[u]=id[(*s.rbegin())];
	if(tag==0)calc(u,-1);
}
signed main(){
	int n=read();
	for(int i=1;i<=n;i++)c[i]=read();
	for(int i=1,u,v;i<n;i++)u=read(),v=read(),add(u,v),add(v,u);
	for(int i=1;i<=n;i++)id[0]+=i,s.insert(0);
	dfs1(1,0);dfs2(1,0,0);
	for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
	return 0;
}

F - 冰火战士

不懂为什么题目说的又臭又长。

这个战斗方式其实就是冰人的前缀和和火人的后缀和取 min,先离散化,然后你发现这个东西可以线段树二分前一个 \(\ge\) 后一个的最后的位置 \(p\),然后就 \(p\)\(p+1\) 你比较一下,然后做完了,\(\mathcal{O}(n\log n)\)。注意答案跟 \(p+1\) 一样的所有温度中 \(p+1\) 不一定是最大的,所以还需要二分一下。

不开 O2 也能过。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
struct IO {
#define MAXSIZE (1 << 20)
#define isdigit(x) (x >= '0' && x <= '9')
  char buf[MAXSIZE], *p1, *p2;
  char pbuf[MAXSIZE], *pp;
#if DEBUG
#else
  IO() : p1(buf), p2(buf), pp(pbuf) {}

  ~IO() { fwrite(pbuf, 1, pp - pbuf, stdout); }
#endif
  char gc() {
#if DEBUG
    return getchar();
#endif
    if (p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin);
    return p1 == p2 ? ' ' : *p1++;
  }
  bool blank(char ch) {
    return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
  }
  template <class T>
  void read(T &x) {
    double tmp = 1;
    bool sign = 0;
    x = 0;
    char ch = gc();
    for (; !isdigit(ch); ch = gc())
      if (ch == '-') sign = 1;
    for (; isdigit(ch); ch = gc()) x = x * 10 + (ch - '0');
    if (ch == '.')
      for (ch = gc(); isdigit(ch); ch = gc())
        tmp /= 10.0, x += tmp * (ch - '0');
    if (sign) x = -x;
  }
  void read(char *s) {
    char ch = gc();
    for (; blank(ch); ch = gc())
      ;
    for (; !blank(ch); ch = gc()) *s++ = ch;
    *s = 0;
  }
  void read(char &c) {
    for (c = gc(); blank(c); c = gc())
      ;
  }
  void push(const char &c) {
#if DEBUG
    putchar(c);
#else
    if (pp - pbuf == MAXSIZE) fwrite(pbuf, 1, MAXSIZE, stdout), pp = pbuf;
    *pp++ = c;
#endif
  }
  template <class T>
  void write(T x) {
    static T sta[35];
    T top = 0;
    do {
      sta[top++] = x % 10, x /= 10;
    } while (x);
    while (top) push(sta[--top] + '0');
  }
  template <class T>
  void write(T x, char lastChar) {
    write(x), push(lastChar);
  }
} io;
struct Que{
	int o,t,x,y;
}q[2000005];
int n,tot,d,N,b[2000005],s[2],a[2][2000005],c[2][8000005];
void build(){
	d=(int)log2(tot+5)+1,N=(1ll<<d)-1;
}
inline void add(int o,int x,int v){
	for(int i=x+N;i>=1;i>>=1)c[o][i]+=v;
}
inline int qry(int o,int l,int r){
	if(l>r)return 0;
	int ans=0;l--,r++;
	for(l+=N,r+=N;(l^r)!=1;l>>=1,r>>=1){
		if(l%2==0)ans+=c[o][l^1];
		if(r%2==1)ans+=c[o][r^1];
	}
	return ans;
}
inline int ask(int k){
	int l=1,r=(1ll<<d),p=1;
	while(l!=r){
		int mid=(l+r)>>1;
		if(c[0][p<<1]+c[1][p<<1]-a[1][mid]<=k)k-=(c[0][p<<1]+c[1][p<<1]),l=mid+1,p=p<<1|1;
		else r=mid,p=p<<1;
	}
	return l;
}
inline int get(int k){
	int l=1,r=(1ll<<d),p=1;
	while(l!=r){
		int mid=(l+r)>>1;
		if(c[1][p<<1]-a[1][mid]<=k)k-=c[1][p<<1],l=mid+1,p=p<<1|1;
		else r=mid,p=p<<1;
	}
	return l;
}
signed main(){
	io.read(n);
	for(int i=1;i<=n;++i){
		int o;io.read(o);
		if(o==1){
			int t,x,y;io.read(t);io.read(x);io.read(y);
			q[i]=(Que){o,t,x,y},b[++tot]=x;
		}
		else{
			int k;io.read(k);
			q[i]=(Que){o,k,-1,-1};
		}
	}
	sort(b+1,b+tot+1);tot=unique(b+1,b+tot+1)-b-1;build();
	for(int i=1;i<=n;++i)if(q[i].o==1)q[i].x=lower_bound(b+1,b+tot+1,q[i].x)-b;
	for(int i=1;i<=n;++i){
		int o=q[i].o,t=q[i].t,x=q[i].x,y=q[i].y,i1,i2;
		if(o==2)x=q[t].x,y=-q[t].y,t=q[t].t;
		s[t]+=y,a[t][x]+=y,add(t,x,y);
		if(!s[0]||!s[1]){io.push('P');io.push('e');io.push('a');io.push('c');io.push('e');io.push('\n');continue;}
		if(a[0][1]>s[1])i1=0,i2=i1+1;
		else if(s[0]<=a[1][tot])i1=tot,i2=i1+1;
		else i1=ask(s[1])-1,i2=i1+1;
		int t1=qry(0,1,i1),t2=qry(1,i2,tot);
		if(t1==0&&t2==0){io.push('P');io.push('e');io.push('a');io.push('c');io.push('e');io.push('\n');continue;}
		if(t1>t2){io.write(b[i1],' ');io.write(t1*2,'\n');continue;}
		if(a[1][tot]==t2)i2=tot;
		else i2=get(s[1]-t2)-1;
		io.write(b[i2],' ');io.write(t2*2,'\n');
	}
	return 0;
}
posted @ 2023-10-08 21:57  xx019  阅读(33)  评论(0编辑  收藏  举报