zoukankan      html  css  js  c++  java
  • 【长沙集训】2017.10.10

    Adore

    1.1 问题描述

    小 w 偶然间遇到了一个 DAG。 这个 DAG 有 m 层,第一层只有1个源点,最后一层只有1个汇点,剩下的每一层都有 k 个 节点。

    现在小 w 每次可以取反第 i(1 < i < n − 1) 层和第 i + 1 层之间的连边。也就是把原本从 (i, k1) 连到 (i + 1, k2) 的边,变成从

    (i, k2) 连到 (i + 1, k1)。 请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条? 答案对 998244353 取模。

    1.2 输入格式

    第一行两个整数 m, k。

    接下来 m − 1 行, 第一行和最后一行有 k 个整数 0 或 1,剩下每行有 k 2 个整数 0 或 1,

    第 (j − 1) × k + t 个整数表示 (i, j) 到 (i + 1, t) 有没有边。

    1.3 输出格式

    一行一个整数表示答案。

    1.4 样例输入

    5 3

    1 0 1

    0 1 0 1 1 0 0 0 1

    0 1 1 1 0 0 0 1 1

    0 1 1

    1.5样例输出

    4

    1.6数据规模与约定

    20% 的数据满足 n ≤ 10, k ≤ 2

    40% 的数据满足 n ≤ 10^3 , k ≤ 2。

    60% 的数据满足 m ≤ 10^3 , k ≤ 5。

    100% 的数据满足 4 ≤ m ≤ 10^4 , k ≤ 10。

     1.

    这要是Noip d1t1我也不用学oi了。。。

    每一层只有10个点,我们只关心每个点的奇偶情况,于是可以考虑状压dp。dp[i][j]表示第i层奇偶情况为j的答案。

    暴力的写法,枚举每一层,每种情况,枚举每条边暴力地更新奇偶算转移到的状态。60分。场上zz不知道哪个地方莫名多了一个忘删掉的for循环,炸了两个点。。

    优化一下,预处理出连向每个点的边的集合,用一个二进制串表示,然后每次和转移来的状态取交,判断这个数的1的个数的奇偶(这一步同样是预处理)。

    然后又是垃圾卡常,,可能我真的是传说中的 真·大常数选手吧。。好不容易卡进去。。

    //Twenty
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    typedef long long LL;
    const LL mod=998244353;
    const int maxn=1e4+299;
    const int maxm=1e6+299;
    int m,k,x,s,t;
    using namespace std;
    int a[maxn][11],b[maxn][11],ok[maxn];
    int f[10005][1030];
    
    inline int get(int &x) {
        int ret=0; char ch=getchar();
        while(ch<'0'||ch>'9') ch=getchar();
        for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0';
        x=ret;
    }
    
    void work() {
        int nn=(1<<k)-1;
        for(register int i=0;i<=nn;i++) ok[i]=ok[i>>1]^(i&1);
        for(register int i=3;i<=m;i++) {
            for(register int j=0;j<=nn;j++)  if(f[i-1][j]){
                int now=0,s1=0,s2=0;
                for(register int l=1;l<=k;l++) {
                    s1|=ok[a[i][l]&j]<<l-1;
                    s2|=ok[b[i][l]&j]<<l-1;
                }
                f[i][s1]+=f[i-1][j];
                if(f[i][s1]>=mod) f[i][s1]-=mod;
                if(i!=m) {f[i][s2]+=f[i-1][j];
                if(f[i][s2]>=mod) f[i][s2]-=mod;}
            }
        }
    }
    
    int main()
    {
        freopen("adore.in","r",stdin);
        freopen("adore.out","w",stdout);
        get(m); get(k);
        int now=0;
        for(register int i=1;i<=k;i++) {
            get(x);
            if(!x) continue;
            now|=(1<<i-1);
        }
        f[2][now]=1;
        for(register int i=2;i<m-1;i++) {
            for(register int j=1;j<=k;j++)
                for(register int l=1;l<=k;l++) {
                    get(x);
                    if(!x) continue;
                    a[i+1][l]|=(1<<j-1);
                    b[i+1][j]|=(1<<l-1);
                }
        }
        for(register int i=1;i<=k;i++) {
            get(x);
            if(!x) continue;
            a[m][1]|=(1<<i-1);
        }
        work();
        printf("%d
    ",f[m][0]);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

    Confess

    2.1 问题描述

    小w 隐藏的心绪已经难以再隐藏下去了。 小w 有 n + 1(保证 n 为偶数) 个心绪,每个都包含了 [1, 2n] 的一个子集。 现在他要找到隐藏的任意两个心绪,使得他们的交大于等于 n/ 2。

    2.2 输入格式

    一行一个整数 n。

    接下来每行一个长度为 k 的字符串,该字符串是一个 64 进制表示,

    ASCII 码为 x 的字符代 表着 x − 33,所有字符在 33 到 33 + 63 之间。

    转为二进制表示有 6k 位,它的前 2n 个字符就是读入的集合,

    第 i 位为 1 表示这个集合包 含 i,为 0 表示不包含。

    2.3 输出格式

    一行两个不同的整数表示两个集合的编号。

    如果无解输出”NO Solution”。

    2.4 样例输入

    10

    EVK#

    IH=#

    676"

    R7,#

    74S"

    6V2#

    O3J#

    S-7$

    NU5"

    C[$$

    3N.#

    2.5 样例输出

    1 2

    2.6 数据规模与约定

    对于 20% 的数据满足 n ≤ 100。

    对于 50% 的数据满足 n ≤ 1 × 10^3。

    对于 100% 的数据满足 n ≤ 6 × 10^3。

     2.

    t1写得太难受+语文太烂,没读懂题,暴力都没打直接输出no solution。

    然而没有No solution的情况,正解就是暴力。

    首先”NO Solution” 两个单词大小写不一致,所以肯定是乱 打的,不会有无解。

    证明?我们不妨算一算如果随机选两个集合,他们交的期望

    min( ∑2n i=1 C(ci ,2) C(n+1,2) | ∑2n i=1 ci = n(n + 1)) = n−1/2

    注意 n 是偶数,所以大于 n−2/2 即可说明会有交为 n 的

    由于大了一个常数,所以至少有 n 对!

    随机 O(n) 对即可

    标解用了bitset,开O2跑得贼快,不用bitset就又是垃圾卡常,怎么卡都卡不进,只能一边输一遍做找到答案就输都不输了才ok。。。

    //Twenty
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    using namespace std;
    const int maxn=6e3+5;
    int n,sz,b[maxn];
    char ch[60005];
    bool a[maxn][2*maxn],tp[10];
    int ok(int x,int y) {
        int ret=0;
        for(int i=1;i<=2*n;i++) {
            if(a[x][i]==1&&a[y][i]==1) ret++;
            if(ret>=n/2) return 1;
        }
        return ret>=n/2;
    }
    int main()
    {
        freopen("confess.in","r",stdin);
        freopen("confess.out","w",stdout);
        scanf("%d",&n);
        for(register int i=1;i<=n+1;i++) {
            cin>>ch;
            int p=0,q=0;
            while(q<=2*n)
            {
                int mid=ch[p++]-33;
                for(int j=0;j<6;j++)
                  a[i][++q]=mid&(1<<j);
            } 
            for(register int j=i-1;j>=1;j--) 
            if(ok(i,j)) {
                printf("%d %d
    ",i,j);
            return 0;
            }
        }
           fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

    --------------------------------------------------------------------------------------------

    事实证明被卡常是某个叫random_shuffle()的函数的锅。换成sort就过了。

    还有getchar会比cin快很多。常数这种东西太可怕了。

    //Twenty
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    using namespace std;
    const int maxn=6e3+5;
    int n,sz,b[maxn],c[maxn];
    char s[105];
    bool a[maxn][2*maxn],tp[10];
    int ok(int x,int y) {
        int ret=0;
        for(int i=1;i<=2*n;i++) {
            if(a[x][i]==1&&a[y][i]==1) ret++;
            if(ret>=n/2) return 1;
        }
        return ret>=n/2;
    }
    
    bool cmp(const int &x,const int &y) {
        return b[x]<b[y];
    }
    
    int main()
    {
        freopen("confess.in","r",stdin);
        freopen("confess.out","w",stdout);
        srand(time(0));
        cin>>n;
        for(int i=1;i<=n+1;i++) {
              int tot=0; 
            char c=getchar();
              while(c<33||c>96)
                c=getchar();
              while(c>=33&&c<=96) {
                  for(int j=0;j<=5&&tot<=2*n;j++)
                  a[i][++tot]=(((c-33)>>j)&1);
                  c=getchar();
              }
        }
        for(int i=1;i<=n+1;i++)  c[i]=i,b[i]=rand();
        sort(c+1,c+n+2,cmp);
        for(int i=1;i<=n+1;i++) {
            for(int j=i+1;j<=n+1;j++)
            if(ok(c[i],c[i+1])) {
                printf("%d %d
    ",c[i],c[i+1]);
                return 0;
            }
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code

     

     Repulsed

    3.1 问题描述

    小 w 心理的火焰就要被熄灭了。 简便起见,假设小w 的内心是一棵 n − 1 条边,n 个节点的树。 现在你要在每个节点里放一些灭火器,每个节点可以放任意多个。 接下来每个节点都要被分配给一个最多 k 条边远的灭火器,每个灭火器最多能分配给 s 个节 点。 最少要多少个灭火器才能让小 w 彻底死心呢?

    3.2 输入格式

    第一行三个整数 n, s, k。

    接下来 n − 1 行每行两个整数表示一条边。

    3.3 输出格式

    一行一个整数表示答案

    3.4 样例输入

    10 10 3

    1 8

    2 3

    1 5

    2 4

    1 2

    8 9

    8 10

    5 6

    5 7

    3.5 样例输出

    1

    3.6 数据规模与约定

    对于 20% 的数据满足 n ≤ 100, k ≤ 2。

    对于另外 20% 的数据满足 k = 1。

    对于另外 20% 的数据满足 s = 1。

    对于 100% 的数据满足 n ≤ 10^5 , k ≤ 20, s ≤ 10

     至底往上贪心。

    考虑一条链上去只要能管得到肯定往上放比较优,每次从下到上再在上面考虑下面的情况。

    g[x][i]表示x点往下i的距离的位置还需要多少灭火器,f[x][i]表示x点往下i的距离的位置有多少没用的灭火器。

    若g[x][k]还不为0,则在该点放灭火器。

    考虑子树中长度为k的距离的位置和k-1距离的位置的互相配放。

    到根节点后再把没用的全用了,还差的一起补上。

    //Twenty
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<vector>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    const int maxn=1e5+299;
    int n,s,k;
    using namespace std;
    
    int ans,ecnt,fir[maxn],nxt[maxn*2],to[maxn*2];
    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 f[maxn][22],g[maxn][22];
    void dfs(int x,int fa) {
        for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa){
            int v=to[i]; dfs(v,x);
            for(int j=1;j<=k;j++) {f[x][j]=min(n,f[x][j]+f[v][j-1]);g[x][j]+=g[v][j-1];} 
        }
        g[x][0]++;
        if(g[x][k]) {
            int tp=(g[x][k]-1)/s+1;
            f[x][0]=tp*s,ans+=tp;
        }
        for(int i=0;i<=k;i++) {
            int j=k-i;
            int d=min(f[x][i],g[x][j]);
            f[x][i]-=d; g[x][j]-=d;
        }
        for(int i=0;i<k;i++) {
            int j=k-i-1;
            int d=min(f[x][i],g[x][j]);
            f[x][i]-=d; g[x][j]-=d;
        }
    }
    
    int main()
    {
        freopen("repulsed.in","r",stdin);
        freopen("repulsed.out","w",stdout);
        scanf("%d%d%d",&n,&s,&k);
        for(int i=1;i<n;i++) {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
        }
        dfs(1,0);
        for(int i=0;i<=k;i++)
            for(int j=0;j<=k;j++) {
                if(i+j<=k) {
                    int d=min(f[1][i],g[1][j]);
                    f[1][i]-=d; g[1][j]-=d;
                }
            }
        int tot=0;
        for(int i=0;i<=k;i++) {
            if(g[1][i]) tot+=g[1][i];
        }
        ans+=(tot?(tot-1)/s+1:0);
        printf("%d
    ",ans);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    View Code
  • 相关阅读:
    vss的ss.ini丢失或损坏导致的vss无法登录错误
    prtvu xsdabljc 视图代码
    安装Ehlib经验
    PHP连接MSSQL
    在Access中实现 case when功能
    快捷输入电大学号 delphi代码
    毕业预警的SP
    新系统班级名称规范化
    查询哪些学生没有做课程注册
    第一个PHP数据库查询应用
  • 原文地址:https://www.cnblogs.com/Achenchen/p/7647431.html
Copyright © 2011-2022 走看看