10.10離線賽

一、字符連通塊
數據:對於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 100007
using 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 200005
using 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啊…………

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章