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)\) 的数量。转移就是
然后你发现 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;
}