【长沙集训】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\n",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\n",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\n",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\n",ans);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
View Code

 

posted @ 2017-10-10 20:43  啊宸  阅读(231)  评论(0编辑  收藏  举报