Codeforces round 396(Div. 2) 题解

Problem A

题目大意

给定两个字符串,要求构造出一个最长的一个串满足:这个串是其中一个串的字序列并且不是另一个串的子序列.输出长度.\((len \leq 10^5)\)

题解

千万年死在读题上
我做题的时候:哇!这不是一道SB题嘛?
2mins敲完,提交Wrong answer
我去,好像看错题了。。我说怎么这道题这么二。。。
我重新看了眼题。。
那这题不更SB了吗??????
一边怀疑自己一边提交了上去,结果Accepted了。。
我去,判个字符串是否相等,再输出长度最大值就可以了

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
char s1[maxn],s2[maxn];
int main(){
    scanf("%s%s",s1+1,s2+1);
    int len1 = strlen(s1+1);
    int len2 = strlen(s2+1);
    if(!strcmp(s1+1,s2+1)) puts("-1");
    else printf("%d\n",max(len1,len2));
    getchar();getchar();
    return 0;
}

Problem B

题目大意

给定一个长为n的序列,判断是否能从这个序列中选出三个数使得这三个数可以组成一个三角形的三边长。\((n \leq 10^5)\)

题解

暴力枚举肯定不可行。
所以我们要有智商地枚举。
我们发现我们其实就是要在这里面选出三个数\(a,b,c\)满足
\(a+b > c ; a+c > b ; b+c > a\)
我们设\(a,b,c\)中最大值为\(a\),那么我们就发现\(a+b < c,a+c < b\)都一定成立
所以我们再选出两个小于等于\(a\)值满足\(b+c < a\)即可
所以我们直接对输入序列排序,顺序考虑\(a\)的取值
取第一个\(\leq a\)和第二个\(\leq a\)的值作为\(b,c\)判定即可

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
int a[maxn];
int main(){
    int n;read(n);
    for(int i=1;i<=n;++i) read(a[i]);
    sort(a+1,a+n+1);
    bool flag = false;
    for(int i=3;i<=n;++i){
        if(a[i-2] + a[i-1] > a[i]){
            flag = true;
            break;
        }
    }
    puts(flag ? "YES" : "NO");
    getchar();getchar();
    return 0;
}

Problem C

题目大意

分别规定\(26\)个字母中每个字母所在的字符串长度不能超过某一值,给定一个长为\(n\)的字符串,将其划分成一些子串,使这个字符串满足上述要求。求:方案数\((mod \text{ } 10^9 + 7)\)、可行划分方案中的最长字串长度、最少划分成多少字串。\((ln \leq 10^5)\)

题解

又是一道字符串。。。
我们看到求方案数+取模就知道这一般不是一道数论题就是一道dp题
我们又看到了这数据范围就可以确定这是一个dp了
我们设\(f[i]\)表示成功划分了\(1~i\)的字符串的方案数,然后我们枚举\(j \in [0,i-1]\)转移即可。
第二问、第三问可以在算方案数的过程中直接记录或用类似过程计算
//没取模挂掉一次

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 1024;
const int mod = 1000000007;
int f[maxn],w[31],g[maxn];
char s[maxn];
inline int id(char ch){return ch - 'a' + 1;}
int main(){
    memset(g,0x3f,sizeof g);
    int n;read(n);
    scanf("%s",s+1);
    for(int i=1;i<=26;++i) read(w[i]);
    f[0] = 1;g[0] = 0;
    int ans = 1;
    for(int i=1;i<=n;++i){
        int lim = w[id(s[i]) ];
        int cnt = 1,j=i-1;
        while(cnt <= lim && j >= 0){
            f[i] += f[j];if(f[i] >= mod) f[i] -= mod;
            g[i] = min(g[i],g[j] + 1);
            ans = max(ans,cnt);
            ++cnt;
            lim = min(lim,w[id(s[j--])]);
        }
    }printf("%d\n%d\n%d\n",f[n],ans,g[n]);
    getchar();getchar();
    return 0;
}

Problem D

题目大意

给定n个词的含义相同或相反的共m个关系,关系具有传递性.每次给定关系的时候先判断此关系是否可以由前面的关系判断,如果不可以则添加此关系,否则判断此关系是否与推断出来的相符。最后询问q组词之间的关系。\((n,m,q \leq 10^5)\)

题解

一看这套路就知道是带权并查集。
每个节点维护父系指针和与父亲的关系。
我们把相反记为1,相同记为0.
每次判断亮点关系时只需要判断一下两点父亲是否相同
若父亲相同则可以推断两点的官司
若两点与父亲的关系均相同或均相反,则两点的关系为相同,否则为相反
至于关系的传递性嘛。。我们发现这个关系的传递性恰好是\(xor\)也就是异或
\(num_{a->c} = num_{a->b}\text{ } xor \text{ }num_{b->c}\),其中\(num_{a->b}\)表示\(a,b\)的关系.
所以我们可以简单地完成路径压缩和查询。

竟然卡我的\(map!!!\)手写了颗\(Trie\)才过的...

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
int fa[maxn],num[maxn];
inline int find(int x){
    int f = x;while(f != fa[f]) f = fa[f];
    int y = fa[x],z;
    while(y != f){
        z = fa[x];
        while(z != f){
            num[x] ^= num[z];
            z = fa[z];
        }fa[x] = f;
        x = y;y = fa[x];
    }return f;
}
const int maxnode = maxn*22 + 10;
int ch[maxnode][27],idx[maxnode],nodecnt;
inline void insert(int x){
    char c;while(c = getchar(),c<'!');
    int nw = 0;
    while(c >= 'a' && c <= 'z'){
        if(ch[nw][c - 'a'] == 0) ch[nw][c - 'a'] = ++nodecnt;
        nw = ch[nw][c - 'a'];
        c = getchar();
    }idx[nw] = x;
}
inline int query(){
    char c;while(c = getchar(),c<'!');
    int nw = 0;
    while(c >= 'a' && c <= 'z'){
        nw = ch[nw][c - 'a'];
        c = getchar();
    }return idx[nw];
}
int main(){
    int n,m,q;read(n);read(m);read(q);
    for(int i=1;i<=n;++i){
        insert(i);
        fa[i] = i;
    }
    for(int i=1,c;i<=m;++i){
        read(c);--c;
        int x = query();
        int y = query();
        //printf("linking %d %d\n",x,y);
        int fx = find(x);
        int fy = find(y);
        if(fx == fy){
            if( (c == 0) == (num[x] == num[y]) ) puts("YES");
            if( (c == 0) != (num[x] == num[y]) ) puts("NO");
        }else{
            fa[fx] = fy;
            if(num[x]^num[y] == c) num[fx] = 0;
            else num[fx] = 1;
            puts("YES");
        }
    }
    while(q--){
        int x = query();
        int y = query();
        int fx = find(x);
        int fy = find(y);
        if(fx != fy) puts("3");
        else printf("%d\n",(num[x] != num[y]) + 1);
    }
    getchar();getchar();
    return 0;
}

Problen E

题目大意

给定一颗点有点权\((val_i \leq 10^6)\)的n个节点的树,定义两点距离为路径上点权异或和。求所有无序点对之间的距离之和。\((n \leq 10^5)\)

题解

因为这道题是对二进制数位进行操作
常规方法不好做的时候应该自然而然地想到对每个数位分别讨论。
我们发现:把每个数位拆开之后,所有的点权都变成了0/1
然后我们要统计的就是xor后为1的数对的个数
我们考虑树形dp
\(f[i][j]\)表示以i为根的子树中到达点i时xor后得到j的节点数目.
即j是0或1
然后我们\(O(n)\)一遍可以算出所有点向上的贡献.
我们再\(dfs\)一遍算出所有点向下的贡献即可.

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
    x=0;char ch;bool flag = false;
    while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
    while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
const int maxn = 100010;
bool w[maxn];
int a[maxn],f[maxn][2];
struct Edge{
    int to,next;
}G[maxn<<1];
int head[maxn],cnt;
void add(int u,int v){
    G[++cnt].to = v;
    G[cnt].next = head[u];
    head[u] = cnt;
}
inline void insert(int u,int v){
    add(u,v);add(v,u);
}
#define v G[i].to
void dfs(int u,int fa){
    f[u][0] = f[u][1] = 0;f[u][w[u]] = 1;
    for(int i = head[u];i;i=G[i].next){
        if(v == fa) continue;
        dfs(v,u);
        f[u][w[u]^0] += f[v][0];
        f[u][w[u]^1] += f[v][1];
    }return;
}ll num;
void dfs(int u,int fa,int zero,int one){
    f[u][w[u]^0] += zero;f[u][w[u]^1] += one;
    num += f[u][1];
    int g[2];
    for(int i=head[u];i;i=G[i].next){
        if(v == fa) continue;
        g[w[u]^1] = f[u][w[u]^1] - f[v][1];
        g[w[u]^0] = f[u][w[u]^0] - f[v][0];
        dfs(v,u,g[0],g[1]);
    }
}
#undef v
int main(){
    int n;read(n);
    for(int i=1;i<=n;++i) read(a[i]);
    for(int i=1,u,v;i<n;++i){
        read(u);read(v);
        insert(u,v);
    }
#define v G[i].to
    ll ans = 0;
    for(int bit=1;bit<=30;++bit){
        num = 0;
        ll x = 0;
        for(int i=1;i<=n;++i){
            w[i] = (a[i] >> (bit-1)) & 1;
            if(w[i] == 1) ++ x;
        }
        dfs(1,0);
        dfs(1,0,0,0);
        num = ((num-x)>>1) + x;
        ans += num<<bit-1;
    }
#undef v
    printf("%lld\n",ans);
    getchar();getchar();
    return 0;
}

在vjudge上做完这些题后去codeforces开了Virtual participation,4分钟AC五道题,真爽!

posted @ 2017-02-13 20:10  Sky_miner  阅读(365)  评论(0编辑  收藏  举报