省选联测 36

瑞平一下。不叠甲了。反正被我瑞平到的那位看到了大概也不会说什么。而且我也不会瑞平别人。

并不是很能理解放到了自己喜欢的歌就高兴得甚至还表现一下的行为。

然而我像神经病一样的行为大概也不能被理解。这也许从另一个方面说明了人类的悲欢并不相通。

说起来我和 joke3579 的音乐品味不能说是基本重合,也只能说是完全不同了。举个例子,《第三心臟》毫无疑问是很不错的,joke3579 也很喜欢。然而我觉得除了一些小地方值得玩味以外就没什么别的了。然而 joke3579 根本不能接受任何核,前些日子给他一些我非常喜欢的非核曲也是对不上他的味。有时候我们两个都很不知道为什么就走了这么长时间。

算了既然人类的悲欢并不相通那似乎没什么可以评价的了。大概生活方式的不同引起的诸多差异是难以避免的。

太空漫步

大水题。有个 corner cases 是开头是 * 或 [ 。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <bitset>
#include <cstring>
using namespace std;
int n,m,pos[110];
char s[110],t[1000010];
int top,stk[110];
bitset<1000010>dp[110];
int main(){
    int tim;scanf("%s%d",s+1,&tim);n=strlen(s+1);
    for(int i=1;i<=n;i++){
        if(s[i]=='[')stk[++top]=i;
        else if(s[i]==']')pos[i]=stk[top],top--;
    }
    while(tim--){
        scanf("%s",t+1);m=strlen(t+1);
        dp[0][0]=1;
        for(int i=1;i<=n;i++){
            dp[i].reset();
            bool tmp=false;
            for(int j=0;j<=m;j++){
                tmp|=dp[i-1][j];
                if(j){
                    if(s[i]=='N'||s[i]=='S'||s[i]=='E'||s[i]=='W')dp[i][j]=dp[i-1][j-1]&(s[i]==t[j]);
                    if(s[i]=='?')dp[i][j]=dp[i-1][j-1];
                }
                if(s[i]=='*')dp[i][j]=tmp;
                if(s[i]=='[')dp[i][j]=dp[i-1][j];
                if(s[i]==']')dp[i][j]=dp[i-1][j]|dp[pos[i]][j];
            }
        }
        for(int i=1;i<=m;i++)putchar(dp[n][i]+'0');puts("");
    }
    return 0;
}

树的解构

小水题。

期望线性性每个节点可以拆开算。观察发现每个节点的贡献只和深度有关。每次往最上边加一条边的贡献是 \(\dfrac 1d\),因此每个节点贡献就是调和级数。

卡常,别写 vector。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int mod=998244353;
int n,ans,inv[2000010];
int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
struct node{
	int v,next;
}edge[2000010];
int t,head[2000010];
void add(int u,int v){
	edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
void dfs(int x,int d){
	ans=(ans+inv[d])%mod;
	for(int i=head[x];i;i=edge[i].next)dfs(edge[i].v,d+1);
}
int main(){
	n=read();inv[1]=1;
	for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++)inv[i]=(inv[i-1]+inv[i])%mod;
	for(int i=2;i<=n;i++){
		int f;scanf("%d",&f);add(f,i);
	}
	dfs(1,1);
	printf("%d\n",ans);
	return 0;
}

小 T 与灵石

根据链的情况,推断每次操作只有距离最远的两个点有贡献。那么找到中点打标记,变成每个点到标记点的最小距离。这个可以处理一个向上的标记一个向下的标记来搞。向上的标记代表下面的标记点的深度最小值,向下的标记同理。最后两次 dfs,第一次下推向上的标记,第二次用向上的标记下推向下的标记,差不多就是个换根。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,q;
struct node{
    int v,next;
}edge[300010];
int t,head[300010],ans[300010],a[1000010],up[300010],down[300010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int num,dfn[300010],rk[300010],fa[300010],top[300010],son[300010],size[300010],dep[300010];
void dfs1(int x,int f){
    dep[x]=dep[f]+1;fa[x]=f;size[x]=1;
    for(int i=head[x];i;i=edge[i].next){
        dfs1(edge[i].v,x);
        size[x]+=size[edge[i].v];
        if(size[son[x]]<size[edge[i].v])son[x]=edge[i].v;
    }
}
void dfs2(int x,int tp){
    dfn[x]=++num;rk[num]=x;top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=son[x])dfs2(edge[i].v,edge[i].v);
    }
}
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]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
int dis(int x,int y){
    return dep[x]+dep[y]-2*dep[lca(x,y)];
}
int getk(int x,int k){
    if(x==1)return x;
    while(k){
        int ret=min(k,dfn[x]-dfn[top[x]]);
        x=rk[dfn[x]-ret];k-=ret;
        if(x==1)return x;
        if(!k)break;
        x=fa[x];k--;
    }
    return x;
}
void dfs3(int x){
    for(int i=head[x];i;i=edge[i].next){
        dfs3(edge[i].v);
        up[x]=min(up[x],up[edge[i].v]);
    }
}
void dfs4(int x){
    down[x]=min(down[x],up[x]-2*dep[x]);
    for(int i=head[x];i;i=edge[i].next){
        down[edge[i].v]=min(down[edge[i].v],down[x]);
        dfs4(edge[i].v);
    }
}
int main(){
    scanf("%d",&n);
    for(int i=2;i<=n;i++){
        int f;scanf("%d",&f);add(f,i);
    }
    dfs1(1,0);dfs2(1,1);
    for(int i=1;i<=n;i++)up[i]=down[i]=2*n;
    scanf("%d",&q);
    while(q--){
        int k;scanf("%d",&k);
        for(int i=1;i<=k;i++)scanf("%d",&a[i]);
        int mx=0,x=a[1],y=a[1];
        for(int i=2;i<=k;i++){
            int d=dis(a[1],a[i]);
            if(mx<d)mx=d,x=a[i];
        }
        mx=0;
        for(int i=1;i<=k;i++){
            int d=dis(x,a[i]);
            if(mx<d)mx=d,y=a[i];
        }
        if(dep[x]<dep[y])swap(x,y);
        int d=dis(x,y);
        int mid=getk(x,d>>1);
        down[mid]=min(down[mid],d-dep[x]);
        if(d&1)up[fa[mid]]=min(up[fa[mid]],dep[x]);
        else up[mid]=min(up[mid],dep[x]);
    }
    dfs3(1);dfs4(1);
    for(int i=1;i<=n;i++)printf("%d\n",down[i]+dep[i]);
    return 0;
}

小 S 埋地雷

loj6611。很神秘的区间 dp。
\(dp_{i,j,t,u}\) 为删掉区间 \([i,j]\),这段区间后第一个比 \([i,j]\) 晚删除的是 \(t\),区间最晚删除的在 \(u\) 之后的答案。

转移枚举最晚删除 \(k\) 拼起来两段区间,那么新增代价是 \(w(i-1,k,j+1,t)\)\(w\) 内分别为 \(i-1,i,i+1,i+2\))。然后枚举左区间的 \(t\)\(v\) 有转移:

\[dp_{i,j,t,u}=\max_{k=u\rightarrow j,v=k+1\rightarrow j+1}(dp_{i,k-1,v,u}+dp_{k+1,j,t,v}+w(i-1,k,j+1,t)) \]

然而转移有坑。具体的,这个 \(u\) 可能是超出区间的。这时候区间最晚删除的数在区间内随便一个都可以了,也就是把 \(u\) 放到左端点。

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int n,dp[80][80][80][80],p[80],q[80],r[80],s[80];
int w(int a,int b,int c,int d){
    return (p[a]-q[b])*(p[a]-q[b])+(p[b]-r[c])*(p[b]-r[c])+(p[c]-s[d])*(p[c]-s[d]);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&p[i]);
    for(int i=1;i<=n;i++)scanf("%d",&q[i]);
    for(int i=1;i<=n;i++)scanf("%d",&r[i]);
    for(int i=1;i<=n;i++)scanf("%d",&s[i]);
    for(int len=1;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            for(int t=j+1;t<=n+1;t++){
                for(int u=i;u<=j;u++){
                    for(int k=u;k<=j;k++){
                        for(int v=k+1;v<=j+1;v++){
                            dp[i][j][t][u]=max(dp[i][j][t][u],dp[i][k-1][v][(u==k)?i:u]+dp[k+1][j][t][(v==j+1)?k+1:v]+w(i-1,k,j+1,t));
                        }
                    }
                }
            }
        }
    }
    printf("%d\n",dp[1][n][n+1][1]);
    return 0;
}
posted @ 2023-02-20 16:48  gtm1514  阅读(31)  评论(3编辑  收藏  举报