2016.10.20 [ TEST 30 ] 总结

来源:互联网 发布:苏州团扇李晶淘宝店 编辑:程序博客网 时间:2024/05/16 05:43

一.第二饭堂
Problem:
定义一个长度为 n 回文串的价值为 k ,当且仅当其长度为 n/2 的前缀和后缀满足价值为 k1 的回文串定义。
根据定义,不是回文串的字符串(包括空串)价值为 0
现在给定一个回文串,要求求出其所有前缀的价值和。
例:
abacaba 的价值
=a(1)+ab(0)+aba(2)+abac(0)+abaca(0)+abacab(0)+abacaba(3)
=6.
Solution:
根据其定义我们可以的出这样一个结论:设fi表示前i个字符组成的字符串的价值,那么易得fi=fi/2+1。利用hashO(n)判断是否是字符串,如果是则更新价值,维护价值同时把答案维护出来。
本题数据不强,int自然溢出即可。
p.s. 某神犇写了马拉车……_ (:3_|<) _厉害了word哥

#include <stdio.h>#include <string.h>#define hash 107long long l, r, pow = 1, ans, f[ 5000005 ], len;char ch[ 5000005 ];int main(){    freopen("hw.in", "r", stdin);    freopen("hw.out", "w", stdout);    scanf("%s", ch+1);    len = strlen( ch+1 );    for(int i = 1; i <= len; i++)    {        l = l * hash + ch[ i ];        r = r + ch[ i ] * pow;        pow *= hash;        if(l == r) f[ i ] = f[ i/2 ] + 1;        ans += f[ i ];    }    printf("%lld\n", ans);    return 0;}

以为用longlong自然溢出会稳一点,结果并无卵用,比int自然溢出慢了50+ms。
二.数字
Problem:
定义一个数字是好数字,当且仅当满足:
1.有 2×n 个数位,n 是正整数(允许好数字有前导0)。
2.构成它的每个数字都在给定的集合 S 中。
3.它的前 n 位之和与后 n 位之和相等 它的奇数位之和与偶数位之和相等。
已知 n , S求合法的好数字个数(mod999983).
Solution:
由条件3合法数字要求前n位之和与后n位之和相等,不妨设fij表示前i个数之和为j的方案数,同时也可以表示i个奇数和为j的方案数。当然其中有重复的方案,设前n个数之和为事件A,n个奇数之和为事件B,由容斥原理可知最终答案为A+BAB,分奇偶两种情况维护一下即可。

#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;#define mod 999983const int inf = ~0u>>1;int n, len_s, c[ 15 ], vis[ 15 ], cnt, maxn = -inf;long long ans, t1, t2, f[ 1001 ][ 10001 ];char s[ 15 ];int main(){    freopen("digit.in","r",stdin);    freopen("digit.out","w",stdout);    scanf("%d", &n);    scanf("%s", s + 1);    len_s = strlen( s+1 );    for(int i = 1; i <= len_s; i++)        if(!vis[ s[ i ] - '0' ])        {            c[ ++cnt ] = s[ i ] - '0';            vis[ s[ i ] - '0' ] = 1;            maxn = max(maxn, c[ cnt ]);        }    f[ 0 ][ 0 ] = 1;    for(int i = 0; i < n; i++)        for(int j = 0; j <= maxn*i; j++)            for(int k = 1; k <= cnt; k++)                    ( f[ i + 1 ][ j + c[ k ] ] += f[ i ][ j ] ) %= mod;    for(int i = 0; i <= maxn * n; i++)        (ans += f[ n ][ i ] * f[ n ][ i ]) %= mod;    (ans <<= 1) %= mod;    int a = (n+1) >> 1;    int b = n >> 1;    for(int i = 0; i <= a * maxn; i++)        (t1 += f[ a ][ i ] * f[ a ][ i ]) %= mod;    for(int j = 0; j <= b * maxn; j++)        (t2 += f[ b ][ j ] * f[ b ][ j ]) %= mod;    ans -= t1 * t2;    printf("%lld\n", (ans % mod + mod) % mod);    return 0;}

三.树上三角形
Problem:
给定一棵 n 个点的点权树,每次进行询问或修改操作,询问操作:每次询问一对 (u,v) ,问是否能在 uv 简单路上(包括 u, v),取得三个点权,以这三个权值为边长构成一个三角形(输出 Y or N)。修改操作:将一个点的权值修改。(点权大小<=2311
Solution:
如果我们考虑选择哪三个点构成三角形,显然这种方法是复杂而不可行的,正难则反,考虑什么样的数列可以使不合法的数尽量多。
假设有三点权值 a1<=a2<=a3 ,那么不合法的方案即为 a1+a2<=a3 ,当 a1+a2=a3 时,可使不合法的区间尽量长,我们可以发现斐波那契数列(1,1,2,3,5…)满足这一性质,由于点权限制在 int 范围内,所以最长不合法序列只有 50 个数。当询问两点之间点的个数大于 50 时直接输出 Y ,小于 50 的暴力验证即可。

#include <stdio.h>#include <algorithm>using namespace std;int n, q, w[ 100001 ], t, a, b, s, e;struct tree{    int to,next;}edge[ 200001 ];int head[ 100001 ],tot;void insert(int x, int y){    edge[ ++tot ].to = y;    edge[ tot ].next = head[ x ];    head[ x ] = tot;    edge[ ++tot ].to = x;    edge[ tot ].next = head[ y ];    head[ y ] = tot;}int fa[ 100001 ],dep[ 100001 ],cnt;long long que[ 100001 ];void dfs(int x, int y){    dep[ x ] = dep[ y ] + 1;fa[ x ] = y;    for(int i = head[ x ]; i; i = edge[ i ].next)        if(edge[ i ].to != y)            dfs(edge[ i ].to,x);}void query(int x, int y){    cnt = 0;    if(dep[ x ] < dep[ y ]) swap(x, y);    while(cnt<=50 && dep[ x ] != dep[ y ])    {        que[ ++cnt ] = w[ x ];        x = fa[ x ];    }    while(cnt <= 50 && x != y)    {        que[ ++cnt ] = w[ x ];        que[ ++cnt ] = w[ y ];        x = fa[ x ];y = fa[ y ];    }    que[ ++cnt ] = w[ x ];    if(cnt >= 50)    {        printf("Y\n");        return;    }    sort(que + 1, que + cnt + 1);    for(int i = 1; i + 2 <= cnt; i++)        if(que[ i ] + que[ i+1 ] > que[ i+2 ])        {            printf("Y\n");            return;        }    printf("N\n");    return;}int main(){    freopen("tree.in","r",stdin);    freopen("tree.out","w",stdout);    scanf("%d%d",&n,&q);    for(int i = 1; i <= n; i++)        scanf("%d", &w[ i ]);    for(int i = 1; i < n; i++)    {        scanf("%d%d", &s, &e);        insert(s, e);    }    dfs(1, 0);    for(int i = 1; i <= q; i++)    {        scanf("%d%d%d", &t, &a, &b);        if(t) w[ a ] = b;        else query(a, b);    }    return 0;}

p.s.注意点权可以卡到int上限,两点权相加可能会爆int

0 0
原创粉丝点击