10.10离线赛

来源:互联网 发布:做篮球数据分析师就业 编辑:程序博客网 时间:2024/05/16 06:01

一、字符连通块
数据:对于70%,n、m∈[1,100]
对于100%,n、m∈[1,1000]

其实很简单,改变一个‘*’变成‘.’,那就会连通其上下左右四个点,那只要一开始判一下连通块,然后标记一下就行了。

二、回文字符序列
数据:对于30%,n∈[1,20]
对于60%,n∈[1,100]
对于100%,n∈[1,2000]

对于30%,可以二进制枚举,然后判选出来的点能否组成回文,这很简单。

后来我先用记忆化搜索,dp[L][R] 表示[L,R]区间内的方案数,然后每次对于一个[L,R]收集一下就行了。这个也不细讲。这样写可以过n∈[1,400]的

从记忆化搜索改成循环dp。按照上面的思想,定义dp[i][j] 表示长度为i起点为j的字符串的方案数。转移其实也很简单,玄学转移
如果A[j]==A[j+i-1],即这段字符串两端相等,那就可以把中间的一段dp值加过来。
所有情况下,要把小区间的dp值转到大区间
这里写图片描述

#include<bits/stdc++.h>#define Mod 100007using namespace std;char A[2005],B[25];int n,dp[2005][2005];int main(){    scanf("%s",A+1);    n=strlen(A+1);    for(int i=1;i<=n;i++)dp[1][i]=1;    for(int i=2;i<=n;i++)        for(int j=1;j+i-1<=n;j++){            if(A[j]==A[j+i-1])dp[i][j]=(dp[i][j]+dp[i-2][j+1]+1)%Mod;//A[j]=A[j+i-1]的情况            dp[i][j]=(dp[i][j]+dp[i-1][j]+dp[i-1][j+1]-dp[i-2][j+1]+Mod)%Mod;//一般情况        }    printf("%d\n",dp[n][1]);//从1开始长度为n    return 0;}

三、删边最小生成树
数据:对于50%,n∈[1,300],m∈[1,3000]
对于100%,n∈[1,5000],m∈[1,200000]

一看完题觉得就和安全路径拿到一模一样。
先变成最小生成树,然后把树外面的边排个序每条边连进去,然后就会形成一个环。
然后每次更新完后把路径压缩一下,用并查集就行了,这样可以少更新一些边。这样就很快了。
其实写暴力也可以过,每次把连进去的边的两个端点一直往上走,边走边更新所有边。

#include<bits/stdc++.h>#define M 200005using namespace std;int n,m,len;struct node1{int x,y,v,id;}A[M];int Fa[M];bool Q[M];int find(int x) {return x==Fa[x]?x:Fa[x]=find(Fa[x]);}bool cmp(node1 a,node1 b){return a.v<b.v;}int fa2[3][M],dep[M],fa[M],ans[M];struct node2{int to,id,v;};vector<node2>edge[M];void f(int x,int fa1,int id,int v){    fa2[0][x]=fa1;fa2[1][x]=id;fa2[2][x]=v;    dep[x]=dep[fa1]+1;    for(int i=0;i<(int)edge[x].size();i++){        int y=edge[x][i].to;        if(y==fa1)continue;        f(y,x,edge[x][i].id,edge[x][i].v);    }}int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}int main(){    scanf("%d %d",&n,&m);    for(int i=1;i<=m;i++)ans[i]=2e9;    for(int i=1;i<=m;i++){scanf("%d %d %d",&A[i].x,&A[i].y,&A[i].v);A[i].id=i;}    sort(A+1,A+m+1,cmp);    for(int i=1;i<=n;i++)Fa[i]=i;    for(int i=1;i<=m;i++){//先把最小生成树造出来        int x=A[i].x,y=A[i].y;        int f1=find(x),f2=find(y);        if(f1==f2)continue;        edge[x].push_back((node2){y,A[i].id,A[i].v});        edge[y].push_back((node2){x,A[i].id,A[i].v});        len+=A[i].v;Q[A[i].id]=1;Fa[f1]=f2;    }    for(int i=1;i<=n;i++)fa[i]=i;    f(1,0,0,0);//造树,并把树上的边的id,权值,端点处理出来    for(int i=1;i<=m;i++){        if(Q[A[i].id])continue;//只枚举不在树上的边        else {            int x=A[i].x,y=A[i].y,v=A[i].v;            while(x!=y){//往上走,并合并                if(dep[x]>dep[y]){                    if(ans[fa2[1][x]]==2e9)ans[fa2[1][x]]=len-fa2[2][x]+v;                    fa[x]=fa2[0][x];                    x=Find(x);                }else{                    if(ans[fa2[1][y]]==2e9)ans[fa2[1][y]]=len-fa2[2][y]+v;                    fa[y]=fa2[0][y];                    y=Find(y);                }            }            ans[A[i].id]=len;//更新        }    }    for(int i=1;i<=m;i++)        if(ans[i]==2e9)printf("%d\n",-1);        else printf("%d\n",ans[i]);    return 0;}

差点就AK了,网站上对了,交上去却只有250啊…………