2013-C21-team5
从 Trac 迁移的文章
这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。
原文章内容如下:
{{{
|
->=+++=<-
T+T
| |
|+|
| |
|+|
| |
|+|
| |
|+|
| |
|+|
V
}}}
{{{
C题
这题不需要用莫比乌斯反演...
令三角形三边长为a、b、c且a<=b<=c
设f(x)为周长为x的三角形的数目,则f(x)可以分为:(1)a<=b==c的情况 与 (2)a<=b<c的情况
第一种情况的数目可以直接算出来:floor(x/2.0)-ceil(x/3.0)
第二种情况a<=b<c,那么我们让c减1,则形式为a<=b<=c的三角形总数,即f(x-1),所以f(x)可以由f(x-1)与第一种情况构成;但是注意:
f(x-1)中有些合法的情况在给c做了加1操作后会变得不合法,即a+b==c+1的情况,需要把这类不合法的情况减掉。
如何计算不合法的情况?因为a+b==c+1且a+b+c+1==x,所以a+b==c+1==x/2,即此时x必然为偶数;所以a+b==x/2,不合法数目为 x/2/2.
f(x)计算好了,接下来看划分。
因为所有三角形都是相似的,所以他们必然起源于同一个原型——即gcd(a,b,c)==1三边互质的三角形。
令周长为x的原型三角形的个数为g(x)。
假设这个原型三角形周长为d,且d|x,那么可以把x分成n/d份,而基于这个原型的所有组合可以用隔板法算出来(n/d-1个空),g(d)的贡献为
g(d)*pow(2, n/d-1)。
g(x)可以由筛法求出,g(x) = f(x) - sigma(g(d)) 其中d|x。
初始所有g(x) == f(x),用类似筛法的方式求即可,总复杂度O(N*lnN)
这个复杂度不同于素数筛法的复杂度,因为宿舍的个数相比N小很多。
代码:
typedef long long llg;
const int N = 5000001;
const int mod = 1000000007;
int pmd[N], g[N];
void solve() {
pmd[0] = 1;
for(int i = 1; i < N; i++) {
pmd[i] = pmd[i-1] << 1;
if(pmd[i] >= mod) pmd[i] -= mod;
}
g[3] = 1;
for(int i = 4; i < N; i++) {
int l, r;
l = (i-1)/2; r = i/3;
if(i%3) ++r;
g[i] = g[i-1] + (l-r+1);
if(g[i] >= mod) g[i] -= mod;
if(!(i & 1)) g[i] -= (i>>2);
if(g[i] < 0) g[i] += mod;
}
for(int i = 3; i < N; i++) {
for(int j = i+i; j < N; j += i) {
g[j] -= g[i];
if(g[j] < 0) g[j] += mod;
}
}
int n, T = 0;
llg ans;
while(scanf("%d", &n) != EOF) {
ans = 0;
for(int d = 1; d <= n; d++) if(n%d == 0) {
ans = ans + ((llg)pmd[d-1]*g[n/d]);
ans %= mod;
}
cout << "Case " << (++T) << ": " << ans <<"\n";
}
}
}}}
{{{
E题
Solution:
借用了kmp的思想.
在运行过程中不断维护一个匹配串pat,将其与str进行匹配.
对于str[i],如果pat怎么也匹配不了的话,则将pat更新为目前为止从pat在str中出现的最晚位置l到i这一段字符串[l,i].
再不断更新整个pat在str中的最晚出现位置.
整个过程中使用了kmp的方法,以保证匹配复杂度为O(n).
具体做法:
ss为原串,pat是用ss[l]~ss[l+k-1]表示的,l为pat目前位置pat出现的最晚位置,k为pat的长度.
a数组存的是pat的自匹配关系.在更新过程中,不断维护数组a.
然后还要注意很多细节,详见代码.
Code:
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
const int INF=10000007;
const int MaxL=100010;
char ss[MaxL];
int a[MaxL],q[MaxL],l[MaxL],len;
int gao(){
int len=strlen(ss);
memset(a,-1,sizeof(a[0])*(len+5));
int l=0,lr=0; //lr表示匹配时pat内字符的位置(kmp的匹配过程)
int i,j,k=1;
for (i=1; i<len; i++){
while (lr>0 && ss[i]!=ss[l+lr]) lr=a[lr-1]+1; //kmp匹配过程
if (ss[i]!=ss[l+lr]){
k=i-l+1; a[k-1]=-1; continue;
//当无法匹配时,将pat更新为l到i.注意,a[k-1]也要更新!(在这里WA了好久)
}
for (j=0; i+j<len; j++)
if (ss[i+j]==ss[l+lr+j]){
if (lr+j+1==k){
l=i-lr; lr=a[lr+j]+1; //更新pat最晚出现位置
i+=j; break;
}
a[i+j-l]=lr+j; //维护数组a, (i+j-l)是相对位置.
}
else{
lr=a[lr+j-1]+1;
i+=j-1; break;
}
}
return len-l;
}
int main(){
int cas=0;
while (scanf("%s",ss)!=EOF){
printf("Case %d: %d\n",++cas,gao());
}
return 0;
}
}}}
{{{
|
->=+++=<-
T+T
| |
|+|
| |
|+|
| |
|+|
| |
|+|
| |
|+|
V
}}}
C题
这题不需要用莫比乌斯反演...
令三角形三边长为a、b、c且a<=b<=c
设f(x)为周长为x的三角形的数目,则f(x)可以分为:(1)a<=b==c的情况 与 (2)a<=b<c的情况
第一种情况的数目可以直接算出来:floor(x/2.0)-ceil(x/3.0)
第二种情况a<=b<c,那么我们让c减1,则形式为a<=b<=c的三角形总数,即f(x-1),所以f(x)可以由f(x-1)与第一种情况构成;但是注意:
f(x-1)中有些合法的情况在给c做了加1操作后会变得不合法,即a+b==c+1的情况,需要把这类不合法的情况减掉。
如何计算不合法的情况?因为a+b==c+1且a+b+c+1==x,所以a+b==c+1==x/2,即此时x必然为偶数;所以a+b==x/2,不合法数目为 x/2/2.
f(x)计算好了,接下来看划分。
因为所有三角形都是相似的,所以他们必然起源于同一个原型——即gcd(a,b,c)==1三边互质的三角形。
令周长为x的原型三角形的个数为g(x)。
假设这个原型三角形周长为d,且d|x,那么可以把x分成n/d份,而基于这个原型的所有组合可以用隔板法算出来(n/d-1个空),g(d)的贡献为
g(d)*pow(2, n/d-1)。
g(x)可以由筛法求出,g(x) = f(x) - sigma(g(d)) 其中d|x。
初始所有g(x) == f(x),用类似筛法的方式求即可,总复杂度O(N*lnN)
这个复杂度不同于素数筛法的复杂度,因为宿舍的个数相比N小很多。
代码:
typedef long long llg;
const int N = 5000001;
const int mod = 1000000007;
int pmd[N], g[N];
void solve() {
pmd[0] = 1;
for(int i = 1; i < N; i++) {
pmd[i] = pmd[i-1] << 1;
if(pmd[i] >= mod) pmd[i] -= mod;
}
g[3] = 1;
for(int i = 4; i < N; i++) {
int l, r;
l = (i-1)/2; r = i/3;
if(i%3) ++r;
g[i] = g[i-1] + (l-r+1);
if(g[i] >= mod) g[i] -= mod;
if(!(i & 1)) g[i] -= (i>>2);
if(g[i] < 0) g[i] += mod;
}
for(int i = 3; i < N; i++) {
for(int j = i+i; j < N; j += i) {
g[j] -= g[i];
if(g[j] < 0) g[j] += mod;
}
}
int n, T = 0;
llg ans;
while(scanf("%d", &n) != EOF) {
ans = 0;
for(int d = 1; d <= n; d++) if(n%d == 0) {
ans = ans + ((llg)pmd[d-1]*g[n/d]);
ans %= mod;
}
cout << "Case " << (++T) << ": " << ans <<"\n";
}
}
E题
Solution:
借用了kmp的思想.
在运行过程中不断维护一个匹配串pat,将其与str进行匹配.
对于str[i],如果pat怎么也匹配不了的话,则将pat更新为目前为止从pat在str中出现的最晚位置l到i这一段字符串[l,i].
再不断更新整个pat在str中的最晚出现位置.
整个过程中使用了kmp的方法,以保证匹配复杂度为O(n).
具体做法:
ss为原串,pat是用ss[l]~ss[l+k-1]表示的,l为pat目前位置pat出现的最晚位置,k为pat的长度.
a数组存的是pat的自匹配关系.在更新过程中,不断维护数组a.
然后还要注意很多细节,详见代码.
Code:
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
typedef long long LL;
typedef pair <int, int> PII;
const int INF=10000007;
const int MaxL=100010;
char ss[MaxL];
int a[MaxL],q[MaxL],l[MaxL],len;
int gao(){
int len=strlen(ss);
memset(a,-1,sizeof(a[0])*(len+5));
int l=0,lr=0; //lr表示匹配时pat内字符的位置(kmp的匹配过程)
int i,j,k=1;
for (i=1; i<len; i++){
while (lr>0 && ss[i]!=ss[l+lr]) lr=a[lr-1]+1; //kmp匹配过程
if (ss[i]!=ss[l+lr]){
k=i-l+1; a[k-1]=-1; continue;
//当无法匹配时,将pat更新为l到i.注意,a[k-1]也要更新!(在这里WA了好久)
}
for (j=0; i+j<len; j++)
if (ss[i+j]==ss[l+lr+j]){
if (lr+j+1==k){
l=i-lr; lr=a[lr+j]+1; //更新pat最晚出现位置
i+=j; break;
}
a[i+j-l]=lr+j; //维护数组a, (i+j-l)是相对位置.
}
else{
lr=a[lr+j-1]+1;
i+=j-1; break;
}
}
return len-l;
}
int main(){
int cas=0;
while (scanf("%s",ss)!=EOF){
printf("Case %d: %d\n",++cas,gao());
}
return 0;
}