• 【自家测试】2018-3-3


    A 题
    Dave 写一个程序,他希望这个程序运行之后可以输出长度为 n 的数组 a。
    在这个程序中 Dave 一开始会开若干个临时变量,并在一开始的时候对某一个进行赋值,然
    后将其输出。
    接下来,Dave 每次可以选 3 个变量(变量可以是相同的),设变量为 bi,bj,bk,然后进行
    如下操作
    1,bi = bj + bk
    2,输出 bi
    由于 Dave 电脑内存极小,现在他想他至少需要多少个临时变量。
    输入
    第一行一个 n 表示数组长度
    第二行 n 个数,表示需要输出的数组
    输出
    一个数,表示变量的个数, 无解输出-1
    输入样例
    5
    1 2 3 6 8
    输出样例
    2
    样例解释
    b1 := 1;
    b2 := b1+b1;
    b1 := b1+b2;
    b1 := b1+b1;
    b1 := b1+b2.
    数据范围
    对于 40%的数据 0<a[i]<=1000
    对于 100%的数据 0<a[i]<10^9 ,n<=23

    奥妙重重的状压dp。

    奥妙之处在于它是一个大概4e7的暴力,一个从1递增的数据就可以卡成3s多,加了一个玄学剪枝可以1s过,结果wa完了。。然后不加剪枝竟然过了??

    奥妙重重

    //Achen
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    const int N=24;
    typedef long long LL;
    using namespace std;
    int n,a[N],isok[N],ck,dp[2][1<<23],vis[1<<23],lastuse[N],inf,ans;
    
    template<typename T>void read(T &x)  {
        char ch=getchar(); x=0; T f=1;
        while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
        if(ch=='-') f=-1,ch=getchar();
        for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    }
    
    int ecnt,fir[N],nxt[N*N],to[N*N];
    void add(int u,int v) {
        nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
    }
    
    #define DEBUG
    int main() {
    #ifdef DEBUG
        freopen("A.in","r",stdin);
        freopen("A.out","w",stdout);
    #endif
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);
        for(int i=2;i<=n;i++) {
            for(int j=1;j<i;j++) 
                for(int k=j;k<i;k++) 
                    if(a[j]+a[k]==a[i]) {
                        add(i,((1<<j-1)|(1<<k-1)));
                        isok[i]=1;
                    }
            ck+=isok[i];
        }
        if(ck<n-1) { puts("-1"); return 0; }
        int o=0;
        memset(dp,127,sizeof(dp));
        inf=dp[0][0]; ans=inf;
        dp[o][1]=1;
        for(int i=2;i<=n;i++) {
            o^=1;
            for(int jj=0,j;jj<(1<<i-2);jj++) {
                j=(jj|(1<<i-2));
                if(dp[o^1][j]!=inf) {
                    for(int k=fir[i];k;k=nxt[k]) if((j&to[k])==to[k]) {
                        vis[j|(1<<i-1)]=i;
                        dp[o][j|(1<<i-1)]=min(dp[o][j|(1<<i-1)],dp[o^1][j]+1);
                        for(int l=j;l;l-=(l&(-l))) {
                            int lb=(l&(-l));
                            vis[(j^lb)|(1<<i-1)]=i;
                            dp[o][(j^lb)|(1<<i-1)]=min(dp[o][(j^lb)|(1<<i-1)],dp[o^1][j]);
                        }
                    }
                }
            }
            for(int j=1;j<(1<<i);j++) {
                if(vis[j]!=i) dp[o][j]=inf;
                else if(i==n) ans=min(ans,dp[o][j]);
                /*if(vis[j]==i) {
                    int cnt=0;
                    for(int l=j;l;l-=(l&(-l))) cnt++;
                    if(cnt>(n-i)*2+1) dp[o][j]=inf;
                }*/
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    /*
    5
    1 2 3 6 8
    
    23
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
    
    23
    1 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
    */
    View Code

    B 题

    有一棵 n 个节点的树,树上的节点从 1 到 n 标号。我们定义一个区间[l,r]的长度为 r-l+1。 对于该树的一棵子树(一个连通子图),我们定义其价值为一个最长的区间[l,r]的长度,并且 所有标号为 l,l+1,...,r 的节点均属于这棵子树。

    现在你需要考虑所有大小不超过 k 的子树,找出一个价值最大的。

    注意在这个问题里面给出 的树是没有根的。

    输入:

    第一行包含两个整数 n,k。

    接下来的 n-1 行每行包含两个整数 ai,bi,表示在 ai 与 bi 间有一条边。

    我们保证这个图构成一棵树。

    输出:

    输出一个整数占一行,表示最大的价值。

    样例输入:

    10 6

    4 10 10

    6 2 9

    9 6 8

    5 7 1

    4 7 7

    3 1 8

    样例输出:

    3

    数据范围:

    对于 10%的数据,1 <= k <= n <= 20;

    对于 30%的数据,1 <= k <= n <= 1000;

    另外有 10%的数据,这棵树是一条链。

    对于 100%的数据,1 <= k <= n <= 100000。

    用两个指针l,r维护l~r形成一个联通块的情况,,开一个set按dfs序维护l~r之间的点,那么目前的lca就是dfs序最小的点和最大点的lca。

    r从1到n扫,加点,当联通块的sz>k时l++,删点。

    加点时加入新点算一遍现在的lca,和从前的lca比较,若有变换,sz加上上一个lca到这个lca之间的点即可。

    若无变化,用树状数组维护子树信息,查询一下当前点的儿子中有没有仍然没被删掉的点,若有则sz不变,否则从set中找到和当前点相邻的点求lca,取深度大的,sz减掉当前点到这个lca之间的点即可。

    删点和加点的方式类似。

    //Achen
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    #include<set>
    const int N=100007;
    typedef long long LL;
    using namespace std;
    int n,k,ans=1,szz,lca;
    
    template<typename T>void read(T &x)  {
        char ch=getchar(); x=0; T f=1;
        while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
        if(ch=='-') f=-1,ch=getchar();
        for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    }
    
    int ecnt,fir[N],nxt[N<<1],to[N<<1],f[N][17],R[N];
    void add(int u,int v) {
        nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
        nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
    }
    
    int sum[N];
    void ADD(int x,int v) { for(int i=x;i<=n;i+=(i&(-i)))     sum[i]+=v; }
    int qry(int x) { int rs=0; for(int i=x;i;i-=(i&(-i))) rs+=sum[i]; return rs; }
    
    int get_lca(int x,int y) {
        if(R[x]<R[y]) swap(x,y);
        for(int i=16;i>=0;i--) 
            if(R[f[x][i]]>=R[y])
                x=f[x][i];
        if(x==y) return x;
        for(int i=16;i>=0;i--) 
            if(f[x][i]!=f[y][i]) 
                x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    
    int dfs_clock,dfn[N],sz[N];
    void dfs(int x,int fa) {
        dfn[x]=++dfs_clock;    
        f[dfn[x]][0]=dfn[fa];
        R[dfn[x]]=R[dfn[fa]]+1; 
        sz[dfn[x]]=1;
        for(int i=1;i<=16;i++) f[dfn[x]][i]=f[f[dfn[x]][i-1]][i-1];
        for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { 
            dfs(to[i],x);
            sz[dfn[x]]+=sz[dfn[to[i]]];
        }
    } 
    
    int inque(int x) { return qry(x+sz[x]-1)-qry(x); }
    
    set<int>s;
    void ins(int x) {
        s.insert(x);
        if(s.size()==1) { ADD(x,1); lca=x; szz=1; return; }
        int nlca=get_lca(*s.begin(),*s.rbegin());
        if(nlca==lca) {
            ADD(x,1);
            if(!inque(x)) {
                int y=(s.lower_bound(x)==s.begin())?0:(*--s.lower_bound(x));
                int z=(++s.lower_bound(x)==s.end())?0:(*++s.lower_bound(x));
                if(!(y*z)) y=z=(y^z);
                y=get_lca(x,y);
                z=get_lca(x,z); 
                if(R[z]>R[y]) swap(z,y); 
                szz+=R[x]-R[y];
            }
        }
        else {
            ADD(x,1);
            szz+=R[x]-R[nlca]+R[lca]-R[nlca];
        }
        lca=nlca;
    }
    
    void del(int x) {
        if(s.size()==1) { s.erase(x);  ADD(x,-1); szz=0; return;}
        s.erase(x);
        int nlca=get_lca(*s.begin(),*s.rbegin());
        s.insert(x);
        if(nlca==lca) {
            if(inque(x)<=1) {
                int y=(s.lower_bound(x)==s.begin())?0:(*--s.lower_bound(x));
                int z=(++s.lower_bound(x)==s.end())?0:(*++s.lower_bound(x));
                if(!(y*z)) y=z=(y^z);
                y=get_lca(x,y);
                z=get_lca(x,z); 
                if(R[z]>R[y]) swap(z,y); 
                szz-=R[x]-R[y];
            }
            ADD(x,-1);
        }
        else {
            ADD(x,-1);
            szz-=R[x]-R[lca]+R[nlca]-R[lca];
        }
        s.erase(x);
        lca=nlca;
    }
    
    void solve() {
        int l=1;
        szz=0; lca=0;
        for(int r=1;r<=n;r++) {
            ins(dfn[r]);
            while(szz>k) 
                del(dfn[l++]);
            ans=max(ans,r-l+1);
        }
    }
    
    #define DEBUG
    int main() {
    #ifdef DEBUG
        freopen("B.in","r",stdin);
        freopen("B.out","w",stdout);
    #endif
        read(n); read(k);
        for(int i=1;i<n;i++) {
            int u,v;
            read(u); read(v);
            add(u,v);
        }
        dfs(1,0);
        solve();
        printf("%d
    ",ans);
        return 0;
    }
    View Code

    C 题
    森林里共有 n 个结点,结点从 1 到 n 编号。森林里共有 m 条有向边,一些结点之间通过有
    向边连接。通过有向边可以从一个结点走到另一个结点,但是通过一条边需要花费其所对应
    的时间。
    有一天,一名农夫闯入了森林。他观察发现,每天都会有一只兔子从 1 号结点跑到 n
    号结点。兔子都很聪明,它总会选择一条从 1 到 n 花费总时间最少的路径,但是兔子每天可
    能会选择不同的路径。农夫仔细研究之后发现无论兔子选择哪条最短路径,都必须经过一些
    特定的结点。也就是说,任何一条从 1 到 n 的最短路都要经过这些结点。于是他只要选择一
    个这样的结点,在那里“守株待兔”便可以抓到兔子了。现在他想知道这样的结点一共有多
    少个。
    输入描述
    第一行两个数 n,m 表示木桩数和边数。
    接下来有 m 行,每行 3 个数 u v w,表示从 u 号木桩到 v 号木桩有一条耗时为 w 的有向边。
    输入保证没有自环和重边。
    输出描述
    如果不能从 1 号木桩到 n 号木桩输出-1,否则输出特定木桩个数。
    输入样例
    5 5
    1 2 2
    1 3 5
    2 4 1
    3 4 4
    4 5 10
    输出样例
    4
    数据范围
    20%的数据 n<=1000,m<=2000
    100%的数据 n<=100000,m<=100000
    1=<u,v<=n,1=<w<=1000

    一种方法是求出起点到每个点的最短路和最短路条数,终点同样,算出经过最短路上的每个点的最短路条数,若是等于总数则计算答案。

    因为最短路会很多,需要hash。

    另一种方法是求出最短路后建出新图求支配点。

    一个过了80的求割点算法。暂时不知道为何会WA。

    //Achen
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<queue>
    #include<cmath>
    const int N=100007;
    typedef long long LL;
    using namespace std;
    int n,m;
    
    template<typename T>void read(T &x)  {
        char ch=getchar(); x=0; T f=1;
        while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
        if(ch=='-') f=-1,ch=getchar();
        for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
    }
    
    int ecnt,fir[N],nxt[N],to[N],val[N];
    int ec,fi[N],nx[N],tt[N],va[N];
    void add(int u,int v,int w) {
        nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;
        nx[++ec]=fi[v]; fi[v]=ec; tt[ec]=u; va[ec]=w;    
    }
    
    deque<int>que;
    int dis[N],dis2[N],vis[N],is[N];
    void spfa(int fir[],int nxt[],int to[],int val[],int dis[]) {
        while(!que.empty()) {
            int x=que.front();
            que.pop_front(); 
            vis[x]=0;
            for(int i=fir[x];i;i=nxt[i]) {
                if(dis[to[i]]>dis[x]+val[i]) {
                    dis[to[i]]=dis[x]+val[i];
                    if(!vis[to[i]]) {
                        vis[to[i]]=1;
                        if(!que.empty()) {
                            if(dis[to[i]]<dis[que.front()]) que.push_front(to[i]); 
                            else que.push_back(to[i]);
                        }
                        else que.push_back(to[i]); 
                    }
                }
            }
        }
    }
    
    int cnt,Fir[N],Nxt[N<<1],To[N<<1]; 
    void ADD(int u,int v) {
        Nxt[++cnt]=Fir[u]; Fir[u]=cnt; To[cnt]=v;
        Nxt[++cnt]=Fir[v]; Fir[v]=cnt; To[cnt]=u;
    }
    
    int dfs_clock,dfn[N],low[N],cut[N],top,rt,rc;
    void tarjan(int x,int rt) {
        low[x]=dfn[x]=++dfs_clock;
        for(int i=Fir[x];i;i=Nxt[i]) {
            if(!dfn[To[i]]) {
                if(x==rt) rc++;
                tarjan(To[i],rt);
                low[x]=min(low[x],low[To[i]]);
                if(x!=rt&&low[To[i]]>=dfn[x]) cut[x]=1;
            }
            else low[x]=min(low[x],dfn[To[i]]);
        }
        if(x==rt&&rc>1) cut[x]=1;
    }
    
    #define DEBUG
    int main() {
    #ifdef DEBUG
        freopen("C.in","r",stdin);
        freopen("C.out","w",stdout);
    #endif
        read(n); read(m);
        for(int i=1;i<=m;i++) {
            int u,v,w;
            read(u); read(v); read(w);
            add(u,v,w);
        }
        memset(dis,127,sizeof(dis));
        memset(dis2,127,sizeof(dis2));
        dis[1]=0; que.push_back(1);
        spfa(fir,nxt,to,val,dis);
        dis2[n]=0; que.push_back(n); 
        spfa(fi,nx,tt,va,dis2);
        for(int i=1;i<=n;i++) 
            if(dis[i]+dis2[i]==dis[n]) is[i]=1; 
        for(int i=1;i<=n;i++) if(is[i]) {
            for(int j=fir[i];j;j=nxt[j]) if(is[to[j]]) {
                ADD(i,to[j]);
            }
        }
        for(int i=1;i<=n;i++) if(is[i]) {
            tarjan(i,i); break;
        } cut[1]=cut[n]=1;
        int ans=0;
        for(int i=1;i<=n;i++) ans+=cut[i];
        printf("%d
    ",ans);
        return 0;
    }
    View Code
  • 相关阅读:
    课时作业
    第八周学习进度
    第一次nabcd编写
    第七周学习进度
    第六天进度
    第五天进度
    第四天进度
    第一篇
    1.Windows 系统下安装 IntelliJ IDEA
    Java开发环境(含IDE)的搭建
  • 原文地址:https://www.cnblogs.com/Achenchen/p/8502830.html
Copyright © 2020-2023  润新知