2017.10.10离线赛总结

str —2992

思路:染色,将每个连通的’.’依依标记id和对应的个数sum。那么答案就是每个’*’的四个方向的连通块sum和。However,每次memset mark[][](判重)是O(n^2)的,所以会超时,也就是只需要for过来…

subpal —2993

思路:区间dp,和完美队形一模一样,但这是求可行的方案数,那么就前缀和再维护一下dp即可。(然而今天就炸在此题,最后无聊把代码放结构体里,结果int main里外都打了int n…)

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define N 2005
#define INF 0x3f3f3f3f
#define LL long long
#define P 100007
// LL mod 
using namespace std;
int n,m;
char s[N];
int ans,sum;
int dp[N];
void Add(int &a,int b){a=(a+b)%P;}
struct P100{
    void solve(){
        REP(i,1,n){
            int cnt=0;
            DREP(j,n,i){
                Add(cnt,dp[j]);
                if(s[i]==s[j]){
                    dp[j]=cnt+1;
                    if(dp[j]>P) dp[j]-=P;
                }
            }
        }
        REP(i,1,n) Add(ans,dp[i]);
        cout<<ans<<endl;
    }
}p100;
int main(){
    scanf("%s",s+1);
    n=strlen(s+1);
    p100.solve(); 
    return 0;
} 

tree —2948

思路:与2968神似,也是询问和维护最值,离线来做。那么先求最小生成树,并mark有用的边,最后倍增一样的套路(当然也可以一个一个up上去,考试没有时间写倍增了…)

#include<bits/stdc++.h>
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;i++)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;i--)
#define N 50005
#define M 200005
#define INF 0x3f3f3f3f
#define LL long long
#define P 100007
// LL mod 调试 文件名 
using namespace std;
int n,m;
bool mark[M];
int fa[N],D[N];
int Id[M],res[M];
struct edge{
    int from,to,cost,id;
    bool operator<(const edge &a)const{
        return cost<a.cost;
    }
}es[M];
struct node{
    int to,cost,id;
};
vector<node>E[N]; 
int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);}
struct P50{
    bool check(){
        REP(i,2,n)if(fa[i]!=fa[i-1])return 0;
        return 1;
    }
    int Kruskal(int w){
        int res=0;
        REP(i,1,n)fa[i]=i;
        REP(i,1,m){
            if(es[i].id==w)continue;
            int x=Find(es[i].from),y=Find(es[i].to);
            if(x==y)continue;
            fa[x]=y;
            res+=es[i].cost; 
        }
        REP(i,1,n)fa[i]=Find(i);
        if(!check())return -1;
        return res;
    }
    void solve(){
        REP(i,1,m){
            int ans=Kruskal(i);
            printf("%d\n",ans);
        }
    }
}p50;
void chkmin(int &x,int y){if(!x || x>y)x=y;} 
struct P100{
    int sum,ans;
    struct Tree{
        int fa,id;
    }tree[N];
    void dfs(int x,int f,int dep){
        REP(i,0,E[x].size()-1){
            node y=E[x][i];
            if(y.to==f)continue;
            D[y.to]=dep+1;
            tree[y.to]=(Tree){x,y.id};
            dfs(y.to,x,dep+1); 
        }
    }
    void Kruskal(){
        REP(i,1,n)fa[i]=i;
        REP(i,1,m){
            int x=Find(es[i].from),y=Find(es[i].to);
            if(x==y)continue;
            fa[x]=y;
            ans+=es[i].cost;
            E[es[i].from].push_back((node){es[i].to,es[i].cost,es[i].id});
            E[es[i].to].push_back((node){es[i].from,es[i].cost,es[i].id});
            mark[es[i].id]=1;
            sum++;
        }
    }
    void Up(int &x,int c){
        chkmin(res[tree[x].id],c);
        x=tree[x].fa;
    }
    void work(int a,int b,int c){
        while(D[a]>D[b])Up(a,c);
        while(D[a]<D[b])Up(b,c);
        while(a!=b)Up(a,c),Up(b,c); 
    }
    void solve(){
        sum=ans=0;
        Kruskal();
        if(sum<n-1){
            REP(i,1,m)puts("-1");
            exit(0);
        }
        dfs(1,0,0);
        REP(i,1,m){
            if(mark[es[i].id])continue;
            work(es[i].from,es[i].to,es[i].cost);
        }
        REP(i,1,m){
            if(!mark[i])printf("%d\n",ans);
            else if(!res[i])puts("-1");
            else printf("%d\n",ans+res[i]-es[Id[i]].cost);
        }
    }
}p100; 
int main(){
    scanf("%d%d",&n,&m);
    REP(i,1,n)fa[i]=i;
    REP(i,1,m)scanf("%d%d%d",&es[i].from,&es[i].to,&es[i].cost),es[i].id=i;
    sort(es+1,es+1+m);
    REP(i,1,m)Id[es[i].id]=i;
    if(m<=3000)p50.solve();
    else p100.solve(); 
//  p100.solve();
    return 0;
}

小结:由于这次第2题又犯了低级错误,而且还是考试最后几分钟改错的,可见最后5分钟还是解放双手,好好检查…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值