zoukankan      html  css  js  c++  java
  • 10.9 做题笔记

    T1

    非常水,处理处前驱后继即可。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 500010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Min(T a,T b){return a<b?a:b;}
    
    int t,n,l[N],r[N];
    ll ans;
    char s[N];
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        read(t);
        for(int i=1;i<=t;i++){
            ans=0;
            read(n);scanf("%s",s+1);
            for(int i=0;i<=n+1;i++) l[i]=r[i]=INF;
            for(int i=1;i<=n;i++){
                if(s[i]=='1') l[i]=0;
                else l[i]=l[i-1]+1;
            }
            for(int i=n;i>=1;i--){
                if(s[i]=='1') r[i]=0;
                else r[i]=r[i+1]+1;
            }
            for(int i=1;i<=n;i++){
                ans+=1ll*Min(l[i],r[i]);
                // printf("i:%d l:%d r:%d
    ",i,l[i],r[i]);
            }
            printf("Case #%d: %lld",i,ans);if(i!=t) puts("");
        }
    }
    

    T2

    这个构造比较巧妙,用到了二进制下的构造,没做出来说明我还是对进制下构造不够熟悉。

    容易发现最长链不会超过 (60),所以可以按照以前题目的套路来做,不过不同的是不能再用拓扑序了。

    考虑二进制,设 (f(x))(x) 最高二进制位。

    对于任意一条极长链,我们发现相邻的两个数的最高二进制位必然不相同,所以我们可以把最高二进制位当做拓扑序,然后按照 (4) 个分,(16) 个分,分别分成小组和大组。因为在最劣情况下( (2) 的幂 )这种构造仍然有效,所以这个题目就做完了。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 1010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    int f[N],n,ans[N][N];
    ll x[N];
    
    inline int F(ll x){
        if(!x) return 1;
        int cnt=0;while(x){x>>=1;cnt++;}return cnt;
    }
    
    int main(){
        read(n);
        for(int i=1;i<=n;i++) read(x[i]);
        for(int i=1;i<=n;i++) f[i]=F(x[i]);
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                if(f[i]/4==f[j]/4) ans[i][j]=1;
                else if(f[i]/16==f[j]/16) ans[i][j]=2;
                else ans[i][j]=3;
            }
        }
        for(int i=1;i<=n-1;i++){
            for(int j=1;j<=i;j++){
                printf("%d ",ans[j][i+1]);
            }
            puts("");
        }
        return 0;
    }
    

    T3

    这个题就差一点就能想出来。做题还是太少。

    首先,不难发现的是如果不连通一定不行。

    否则,最终状态是确定的,我们可以得到最终状态。

    那么我们考虑从最终状态如何到达初始状态。

    不难发现,这就是一个最长公共子串问题。

    注意是环,且翻转同构。

    因为两边都是 (n) 的阶乘,所以可以转化成最长上升子序列。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 1010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Max(T a,T b){return a<b?b:a;}
    template<typename T> inline T Min(T a,T b){return a<b?a:b;}
    
    struct edge{
        int to,next;
        inline void Init(int to_,int ne_){
            to=to_;next=ne_;
        }
    }li[N<<1];
    int head[N],tail;
    
    inline void Add(int from,int to){
        li[++tail].Init(to,head[from]);
        head[from]=tail;
    }
    
    int n,b[N],bt,c[2][N],a[N];
    
    bool vis[N];
    inline void Dfs(int k){
        vis[k]=1;b[++bt]=k;
        for(int x=head[k];x;x=li[x].next){
            int to=li[x].to;
            if(vis[to]) continue;
            Dfs(to);
        }
    }
    
    int f[N],rk[N],ans=INF;
    
    //返回 a 和 c[id] 的 LIS
    inline int GetLIS(int id){
        int len=1;
        for(int i=1;i<=n;i++) a[i]=b[i];
        for(int i=1;i<=n;i++) rk[c[id][i]]=i;
        for(int i=1;i<=n;i++) a[i]=rk[a[i]];
        f[len]=a[1];
        for(int i=2;i<=n;i++){
            if(a[i]>f[len]) f[++len]=a[i];
            else{
                int w=lower_bound(f+1,f+len+1,a[i])-f;
                f[w]=a[i];
            }
        }
        return len;
    }
    
    inline void Turn(){
        int now=b[1];
        for(int i=2;i<=n;i++) b[i-1]=b[i];
        b[n]=now;
    }
    
    inline void Clear(){
        ans=INF;bt=0;
        for(int i=1;i<=n;i++) head[i]=0;
        for(int i=1;i<=n;i++) vis[i]=0;
        tail=0;
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        while(scanf("%d",&n)!=EOF&&n){
            for(int i=1;i<=n;i++){
                int a,b;read(a);read(b);
                Add(i,a);Add(i,b);
            }
            for(int i=1;i<=n;i++){
                c[0][i]=i;c[1][i]=n-i+1;
            }
            Dfs(1);
            if(bt!=n){
                puts("Not solvable.");Clear();
                continue;
            }
            puts("Knot solvable.");
            for(int i=1;i<=n;i++){
                int now1=GetLIS(0);
                int now2=GetLIS(1);
                ans=Min(ans,Min(n-now1,n-now2));
                Turn();
            }
            printf("%d
    ",ans);
            Clear();
        }
        return 0;
    }
    

    T4

    不难想到的是我们枚举两条边,同时维护 (size)。能够拿到 (50) 分。不过这个不能再继续优化了。

    我们考虑另一种做法,也就是说我们改为枚举点,枚举某个点就相当于断开其与父亲的连边。

    (s(x)) 表示节点 (x) 的子树大小,这里我们默认以 (1) 为根。

    如果 (y)(x) 的祖先,那么三个连通块大小就是 (s(1)-s(y),s(y)-s(x),s(x))

    否则,三个连通块大小就是 (s(1)-s(x)-s(y),s(x),s(y))

    我们枚举 (y),考虑寻找 (x)。通过计算得知(就是把带绝对值的式子列出来然后划到数轴上)对于祖先关系,(s(x)) 应该是要最接近 (frac{s(1)+s(x)}{2}),而下面这个是 (frac{s(1)-s(x)}{2})

    这其实我们维护所有可行 (x) 的集合。我们设法维护这个集合并放在 (set) 里面,然后在里面二分查找就可以把复杂度降至 (O(nlog n))

    我们关注一下 dfs 过程,设 (A) 集合表示已经遍历的还未回溯的点,(B) 集合表示已经遍历且已经回溯的点。

    对于一个 (k),我们在回溯到 (k) 的时候在 (A) 里找对应 (x)。这个是更新祖先关系的答案。

    对于一个 (k),我们在遍历到 (k) 的时候在 (B) 里找对应 (x)。这个是更新并列关系的答案。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 200010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Min(T a,T b){return a<b?a:b;}
    template<typename T> inline T Max(T a,T b){return a<b?b:a;}
    
    struct edge{
        int from,to,next;
        inline void Init(int fr_,int to_,int ne_){
            from=fr_;to=to_;next=ne_;
        }
    }li[N];
    int head[N],tail;
    
    inline void Add(int from,int to){
        li[++tail].Init(from,to,head[from]);
        head[from]=tail;
    }
    
    int Siz[N],flag=-1,n;
    int none;
    
    inline void dfs(int k,int fa){
        Siz[k]=1;
        for(int x=head[k];x;x=li[x].next){
            if(flag==x||x==flag+1) continue;
            int to=li[x].to;assert(to!=none);
            if(to==fa) continue;
            dfs(to,k);Siz[k]+=Siz[to];
        }
    }
    
    int ans1=INF,ans2=-INF,ans=INF;
    
    inline void Dp(int rt,int k,int fa){
        for(int x=head[k];x;x=li[x].next){
            if(x==flag||x==flag+1) continue;
            int to=li[x].to;
            if(to==fa) continue;
            int now1=Siz[rt]-Siz[to],now2=Siz[to];
            if(abs(now1-now2)<abs(ans1-ans2)){
                ans1=now1;ans2=now2;
            }
            Dp(rt,to,k);
        }
    }
    
    inline void Update(int val){
        ans=Min(ans,Max(ans1,Max(ans2,val))-Min(ans1,Min(ans2,val)));
    }
    
    inline void Solve(){
        for(int i=1;i<=tail;i+=2){
            flag=i;
            none=li[i].to;dfs(li[i].from,-1);
            none=li[i].from;dfs(li[i].to,-1);
            ans1=INF;ans2=-INF;
            Dp(li[i].from,li[i].from,-1);
            Update(Siz[li[i].to]);
            ans1=INF;ans2=-INF;
            Dp(li[i].to,li[i].to,-1);
            Update(Siz[li[i].from]);
        }
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        read(n);
        for(int i=1;i<=n-1;i++){
            int from,to;read(from);read(to);
            Add(from,to);Add(to,from);
        }
        Solve();
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    [HDU 3038] How Many Answers Are Wrong
    [BZOJ 4977][Lydsy1708月赛]跳伞求生
    [BZOJ4974] 字符串大师
    总结-exCRT
    [luogu 4777] exCRT
    [AHOI 2009] 中国象棋
    JavaScript MVC框架PK:Angular、Backbone、CanJS与Ember
    十一黄金周 加班加点随笔
    从两个设计模式到前端MVC-洪宇
    Todo&Rocket
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15389850.html
Copyright © 2011-2022 走看看