一.第二饭堂
Problem:
定义一个长度为
n
回文串的价值为
根据定义,不是回文串的字符串(包括空串)价值为
0
。
现在给定一个回文串,要求求出其所有前缀的价值和。
例:
=a(1)+ab(0)+aba(2)+abac(0)+abaca(0)+abacab(0)+abacaba(3)
=6.
Solution:
根据其定义我们可以的出这样一个结论:设
fi
表示前
i
个字符组成的字符串的价值,那么易得
本题数据不强,
int
自然溢出即可。
p.s. 某神犇写了马拉车……_ (:3_|<) _厉害了word哥
#include <stdio.h>
#include <string.h>
#define hash 107
long 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
是正整数(允许好数字有前导
2.构成它的每个数字都在给定的集合
S
中。
3.它的前
已知
Solution:
由条件3合法数字要求前
n
位之和与后
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define mod 999983
const 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
个点的点权树,每次进行询问或修改操作,询问操作:每次询问一对
Solution:
如果我们考虑选择哪三个点构成三角形,显然这种方法是复杂而不可行的,正难则反,考虑什么样的数列可以使不合法的数尽量多。
假设有三点权值
a1<=a2<=a3
,那么不合法的方案即为
a1+a2<=a3
,当
a1+a2=a3
时,可使不合法的区间尽量长,我们可以发现斐波那契数列(1,1,2,3,5…)满足这一性质,由于点权限制在
int
范围内,所以最长不合法序列只有
50
个数。当询问两点之间点的个数大于
50
时直接输出
Y
,小于
#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 。