zoukankan      html  css  js  c++  java
  • 【胡策08】解题报告

    【第一题】

    题意:

    给一个 01 串设为其 S,询问是否存在只出现两次的 01 串 T。

    这里的出现定义为存在一串下标 a_1,a_2,...,a_m,满足 a_1<a_2<...<a_m 且 S_{a_i}=T_i

    2≤n≤5000,数据随机。

    题解:

    很容易想到部分分算法DFS枚举子集。

    由于数据随机,n>10时大概率存在,直接输出。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    /*------------------------------------------------------------*/
    const int inf=0x3f3f3f3f;
    
    int n,a[20][3000];bool ok[20],b[20];
    char s[20];
    void dfs(int x)
    {
        if(x==n+1)
        {
            int ans=0,tot=0;
            for(int i=1;i<=n;i++)if(ok[i])
            {
                tot++;
                ans=ans*2+b[i];
            }
            a[tot][ans]++;
        }
        else
        {
            ok[x]=0;
            dfs(x+1);
            ok[x]=1;
            dfs(x+1);
        }
    }
    int main()
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        if(n>10){printf("Y");return 0;}
        for(int i=1;i<=n;i++)if(s[i]=='1')b[i]=1;else b[i]=0;
        memset(a,0,sizeof(a));
        dfs(1);
        bool ans=0;
        for(int i=1;i<=n;i++)
        for(int j=0;j<=1100;j++)
        if(a[i][j]==2)ans=1;
        if(ans)printf("Y");else printf("N");
        return 0;
    }
    DFS

    O(n)的写法应该是找0110或1001?暂时理解不了……(> <)

    【第二题】

    题意:

    给长度为 n 的数列 A 和长度为 m 的数列 B,问有多少长度为 m 的数列 C 满足

    1 leq C_1<C_2<...<C_mleq n

    (A_{c_1}+B_1) leq (A_{c_2}+B_2) leq ... leq (A_{c_m}+B_m)

    n≤2000,m≤1000

    题解:

    很容易想到部分分算法DP。

    f[i][j]=Σf[k][j-1],k<i&&满足条件

    复杂度O(n*m*n),考虑优化。

    改变枚举顺序,将j作为第一维枚举,用树状数组维护。

    令c为a重排序后数组,由于条件为c[k]+b[j-1]<=c[i]+b[j],其中排序后k递增,就可以从小到大维护每个值对应的转移来源上限g[i],方便待会查询时映射过来。

    上面过程限制数值大小,然后用树状数组1~n查一个插一个限制坐标大小。

    复杂度O(m*n*log(n))。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    /*------------------------------------------------------------*/
    const int inf=0x3f3f3f3f,maxn=2010,MOD=1000000007;
    
    int n,m,tot,t[maxn],a[maxn],b[maxn],c[maxn],d[maxn],f[maxn],g[maxn];
    int mods(int x){return x>=MOD?x-MOD:x;}
    struct node{int x,id;}cyc[maxn]; 
    int lowbit(int x){ return x&(-x);}
    void modify(int x,int y){ while(x<=tot) t[x]=mods(t[x]+y),x+=lowbit(x);}
    int query(int x){ int s=0; while(x) s=mods(s+t[x]),x-=lowbit(x); return s;}
    
    bool cmp(node a,node b){return a.x<b.x;}
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){scanf("%d",&a[i]);cyc[i].x=a[i];cyc[i].id=i;}
        for(int i=1;i<=m;i++)scanf("%d",&b[i]);
        sort(cyc+1,cyc+n+1,cmp);
        for(int i=1;i<=n;i++)if(cyc[i].x==cyc[i-1].x){d[cyc[i].id]=tot;}else{c[++tot]=cyc[i].x;d[cyc[i].id]=tot;}
        for(int i=1;i<=n;i++)f[i]=1;
        for(int j=2;j<=m;j++)
        {
            g[0]=0;
            for(int i=1;i<=tot;i++)
            {
                g[i]=g[i-1];
                while(g[i]+1<=tot&&c[g[i]+1]+b[j-1]<=c[i]+b[j])g[i]++;
            }
            memset(t,0,sizeof(t));
            modify(d[j-1],f[j-1]);
            for(int i=j;i<=n;i++)
            {
                int tmp=f[i];
                f[i]=query(g[d[i]]);
                modify(d[i],tmp);
            }
        }
        int ans=0;
        for(int i=m;i<=n;i++)ans=mods(ans+f[i]);
        printf("%d",ans);
        return 0;
    }
    DP+树状数组

    【第三题】

    题意:给一个图,n 个点 m 条双向边,每条边有其长度。n 个点中有 k 个是特殊点,问任意两个特殊点的最短路是多少。

    n≤10^5,m≤3*10^5,k≤10^4。

    题解:

    考试时想到其实一遍dijkstra理论上已经可以得到全图信息,不应该需要k次。

    结合dijkstra可以设置多源最短路(起点集),想到了设置多起点然后记录每个点的最短路和次短路(维护它们来自不同的特殊点),一边统计答案。

    复杂度O(m log n)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cctype>
    #include<queue>
    using namespace std;
    const int maxn=100010,maxm=600010,inf=0x3f3f3f3f;
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    struct edge{int from,v,w;}e[maxm];
    struct Node{int x,d,id;}cyc;
    int n,m,first[maxn],tot,d[maxn],t,k,s[10010],ans,d2[maxn],g[maxn],g2[maxn];
    priority_queue<Node>q;
    bool operator <(Node a,Node b)
    {return a.d>b.d;}
    void insert(int u,int v,int w)
    {tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    void dijkstra()
    {
        for(int i=1;i<=n;i++){d[i]=inf;d2[i]=inf;g[i]=g2[i]=0;}
        for(int i=1;i<=k;i++)
        {
            d[s[i]]=0;g[s[i]]=s[i];
            cyc.x=s[i],cyc.d=0;cyc.id=s[i];q.push(cyc);
        }
        while(!q.empty())
         {
            cyc=q.top();q.pop();
            if(cyc.d!=d[cyc.x])continue;
            int x=cyc.x;
            for(int i=first[x];i;i=e[i].from)
            if(d[e[i].v]!=inf)
            {
                if(g[e[i].v]!=cyc.id)ans=min(ans,d[e[i].v]+e[i].w+d[x]);else ans=min(ans,d2[e[i].v]+e[i].w+d[x]);
                if(d[e[i].v]>d[x]+e[i].w&&g[e[i].v]!=cyc.id)
                {
                    d2[e[i].v]=d[e[i].v];
                    g2[e[i].v]=g[e[i].v];
                    d[e[i].v]=d[x]+e[i].w;
                    g[e[i].v]=cyc.id;
                    cyc.x=e[i].v,cyc.d=d[e[i].v];
                    q.push(cyc);
                }
                else if(d2[e[i].v]>d[x]+e[i].w&&g[e[i].v]!=cyc.id)
                {
                    d2[e[i].v]=d[x]+e[i].w;
                    g2[e[i].v]=cyc.id;
                }
            }
            else
            {
                d[e[i].v]=d[x]+e[i].w;g[e[i].v]=cyc.id;
                cyc.x=e[i].v,cyc.d=d[e[i].v];
                q.push(cyc);
            }
         }
    }
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=k;i++)s[i]=read();
        for(int i=1;i<=m;i++)
         {
             int u=read(),v=read(),w=read();
             insert(u,v,w);
             insert(v,u,w);
         }
        ans=inf;
        dijkstra();
        printf("%d",ans);
        return 0; 
    }
    dijkstra

    正解:

    考虑更简单的情况,若每条边没有边权,显然用BFS,那么第一个访问了两次的点路程相加就是答案。

    出现边权后,不能使用BFS的原因在于边权不一,有大小边之分。

    我们考虑使用优先队列维护,每次处理距离值最小的点,如此便达到了BFS后面访问的点距离值大于前面访问的点的目的。

    于是成功实现了带边权图的BFS。

    但是由于边权大小不一,不能认定第一个两次访问的结点为答案了,所以把全图bfs完后确定答案。

    此时,对于一个点,访问到它的一定是最短和次短,再保证来自两个不同的特殊点,就可以实现访问结点两次就统计答案后不再访问。

    复杂度O(m log n)

    回来观光一波,发现这个解法,描述的就是dijkstra的原理。

    到达每个点的最长路径+来自不同点的次长路径贡献答案,一定能统计到。

    由于dijkstra特有的从小到大路径长度保证,所以这样是正确的。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int maxn=100010,maxm=600010,inf=0x3f3f3f3f;
    struct edge{int v,from,w;}e[maxm];
    struct cyc{
        int x,y,d;
        bool operator < (const  cyc &x) const
        {return d>x.d;}
    };
    priority_queue<cyc>q;
    int n,first[maxn],m,k,s[maxn],d[maxn],b[maxn],tot;
    bool vis[maxn];
    void insert(int u,int v,int w)
    {tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    int main()
    {
        scanf("%d%d%d",&n,&m,&k);
        for(int i=1;i<=k;i++)scanf("%d",&s[i]);
        for(int i=1;i<=m;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            insert(u,v,w);
            insert(v,u,w);
        }
        memset(d,0x3f,sizeof(d));
        for(int i=1;i<=k;i++){q.push((cyc){s[i],s[i],0});/*d[i]=0;*/}
        memset(vis,0,sizeof(vis));
        int ans=inf;
        while(!q.empty())
        {
            cyc x=q.top();q.pop();
            if(d[x.x]!=inf&&b[x.x]!=x.y){ans=min(ans,d[x.x]+x.d);vis[x.x]=1;}
            else
            {
                d[x.x]=x.d;b[x.x]=x.y;
                for(int i=first[x.x];i;i=e[i].from)
                if(b[e[i].v]!=x.y&&!vis[e[i].v])q.push((cyc){e[i].v,x.y,x.d+e[i].w});
            }
        }
        printf("%d",ans);
        return 0;
    }
    正解

    PS:dijkstra常数比正解小=w=

  • 相关阅读:
    分享一下用终端的命令来恢复丢失的硬盘分区表 (转)
    Smart Link
    underrun || overrun
    mtr命令详解诊断网络路由
    tracert traceroute
    OE1、OE2、ON1、ON2路由有什么区别?
    GRE tunnel 2
    【SAP HANA】新建账户和数据库(2)
    【SAP HANA】SAP HANA开篇(1)
    入职一周
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7246315.html
Copyright © 2011-2022 走看看