【NOIP2015D2】 解题报告

这次模拟考试订正历程很悲催……

Solution

T1:很经典的二分答案,直接切掉
T2:听说过是DP,考试时先写了一个dfs,发现要记录前一个有没有选来看后面的是否连在一起,f[i][j][k][sta]表示A数组前i个,B数组前j个,分成k段,最后两个是否匹配的方案数,一开始忘记选了也可以分为2段,总的方程就是比较a[i+1]和b[j+1],若不同,只能推到f[i+1][j][k][0],若相同,a[i+1]可以不选,就是f[i+1][j][k][0],选,但是自成一段,a[i+1][j+1][k+1][1],或者与前面的合并,但是a[i]得选,f[i+1][j+1][k][1]。
T3:其实一开始不会,一直想不出去掉一条边后怎么计算所有的路径并统计,只会暴力的50分,后来看了题解……发现一开始思路错了,应该想二分,去判断比较简单,就把所有的不符合的路径拿出来,然后记录,然后取所有路径的交集中最长的一条边,看不符合条件的最长的一条路径是否符合即可,思路还算简单。实现上要用树上差分,就是对于路径(u,v),tmp[u]++,tmp[v]++,tmp[lca(u,v)]-=2,tmp[i]表示从i开始向上到根的路径有多少,统计的时候自底向上计算,实现是DFS,先算儿子,父亲tmp+=儿子tmp,记录tmp=cnt的节点,最后判断即可。

订正时遇到的问题:一直WA,吐血的那种,最后发现有些代码没有dfs(v,u),有些代码lca的k只到2(调试时改的),我去。

CODE

T1:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=50010;
int a[MAXN],L,n,m;

bool check(int k)
{
    int last=0,cnt=0;
    for (int i=1;i<=n;i++)
        if (a[i]-last<k) cnt++;
        else last=a[i];
    if (L-last<k) cnt++;
    return cnt<=m;   
}
int main()
{
    scanf("%d%d%d",&L,&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    int l=0,r=L;
    while (l<r)
    {
        int mid=(l+r+1)>>1;
        if (check(mid)) l=mid;
        else r=mid-1;       
    }
    printf("%d\n",l);   
}

T2:

#include<bits/stdc++.h>
using namespace std;
#define MOD 1000000007

char a[1010],b[210];
int f[2][205][2][205],n,m,K,ans,now;


int main()
{
    scanf("%d%d%d",&n,&m,&K);
    scanf("%s",a+1);
    scanf("%s",b+1);
    n=strlen(a+1); m=strlen(b+1);
    f[0][0][0][0]=1; now=0;
    for (int i=0;i<n;i++)
    {
        for (int j=0;j<=m;j++)
            for (int k=0;k<=K;k++)
                for (int sta=0;sta<=1;sta++)
                {
                    int p=f[now][j][sta][k];
                    if (!p) continue;
                    f[1-now][j][0][k]=(f[1-now][j][0][k]+p)%MOD;
                    if (j<m && a[i+1]==b[j+1]) 
                    {
                        if (sta) f[1-now][j+1][1][k]=(f[1-now][j+1][1][k]+p)%MOD;
                        f[1-now][j+1][1][k+1]=(f[1-now][j+1][1][k+1]+p)%MOD;
                    }
                    f[now][j][sta][k]=0;
                }
        now=1-now;
    }
    ans=(f[now][m][0][K]+f[now][m][1][K])%MOD;
    printf("%d\n",ans);
    return 0;
}

T3:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=300010,MAXM=300010;
int Head[MAXN],tot,n,m,maxdeep,cnt,limit,ans;
int fa[MAXN],f[21][MAXN],deep[MAXN],dis[MAXN],tmp[MAXN];

struct Edge{
    int v,d,next;
}edge[MAXN*2];
struct node{
    int u,v,d,lca;
}e[MAXM];

void addedge(int x,int y,int z)
{
    edge[++tot]=(Edge){y,z,Head[x]};
    Head[x]=tot;
}
void dfs_pre(int u,int pre)
{
    maxdeep=max(maxdeep,deep[u]);
    for (int i=Head[u];i;i=edge[i].next)
    {
        int v=edge[i].v,d=edge[i].d;
        if (v==pre) continue;
        fa[v]=u; deep[v]=deep[u]+1; dis[v]=dis[u]+d;
        dfs_pre(v,u);
    }   
}
int query_lca(int u,int v)
{
    if (deep[u]<deep[v]) swap(u,v);
    int step=deep[u]-deep[v];
    for (int k=0;k<=20;k++)
        if (step & (1<<k)) u=f[k][u];
    for (int k=20;k>=0;k--)
        if (f[k][u]!=f[k][v]) 
            u=f[k][u],v=f[k][v];
    return u==v?u:fa[u];    
}
void calc_lca()
{
    memset(f,-1,sizeof(f));
    int k=log(n)/log(2);
    for (int i=1;i<=n;i++) f[0][i]=fa[i];
    for (int j=1;j<=k;j++)
        for (int i=1;i<=n;i++)
            if (f[j-1][i]!=-1) f[j][i]=f[j-1][f[j-1][i]];
    for (int i=1;i<=m;i++)
    {
        int u=e[i].u,v=e[i].v;
        e[i].lca=query_lca(u,v);
        e[i].d=dis[u]+dis[v]-2*dis[e[i].lca];
    }
}
void init()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }
    for (int i=1;i<=m;i++)
        scanf("%d%d",&e[i].u,&e[i].v),e[i].d=0;
    fa[1]=-1; deep[1]=1; dis[1]=0;
    dfs_pre(1,1); 
    calc_lca();
}
int dfs(int u,int pre)
{
    for (int i=Head[u];i;i=edge[i].next)
    {
        int v=edge[i].v,d=edge[i].d;
        if (v==pre) continue;
        dfs(v,u);
        if (tmp[v]==cnt) ans=max(ans,d);
        tmp[u]+=tmp[v];
    }
    return ans;
}
bool check(int mid)
{
    memset(tmp,0,sizeof(tmp));
    cnt=0,limit=0;
    for (int i=1;i<=m;i++)
        if (e[i].d>mid)
        {
            cnt++;
            tmp[e[i].u]++;
            tmp[e[i].v]++;
            tmp[e[i].lca]-=2;
            limit=max(limit,e[i].d-mid);
        }
    if (!cnt) return true;
    ans=-1; dfs(1,0);
    return ans>=limit;  
}
void solve()
{
    int l=0,r=300000000;
    while (l<r)
    {
        int mid=(l+r)>>1;
        if (check(mid)) r=mid;
        else l=mid+1;       
    }
    printf("%d\n",r);   
}
int main()
{
    //freopen("transport.in","r",stdin);
    //freopen("transprot.out","w",stdout);
    init();
    solve();    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值