2017北京国庆刷题Day3 morning

 期望得分:100+60+0=160

 实际得分:100+30+0=130

考场上用的哈希

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mod 1000003
#define mod2 100003
char s[10000001];
bool vis[mod],vis2[mod2];
int hash1,hash2;
int gethash()
{
    int len=strlen(s);
    sort(s,s+len);
    hash1=s[0]-'A'+1,hash2=hash1;
    for(int i=1;i<len;i++)
    {
        hash1=(hash1*26+(s[i]-'A')+1)%mod;
        hash2=(hash2*26+(s[i]-'A')+1)%mod2;
    }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    int n,ans=0;
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",s);
        gethash();
        if(!vis[hash1] || !vis2[hash2]) vis[hash1]=true,vis2[hash2]=true,ans++; 
    }
    printf("%d",ans);
}
myself

std直接sort,map 判重结构体

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>

using namespace std;

struct node
{
       int z[30];
       node()
       {
             memset(z,0,sizeof(z));
       }
       bool operator<(const node &a)const
       {
            for (int b=1;b<=26;b++)
            if (z[b]!=a.z[b]) return z[b]<a.z[b];
            return false;
       }
}now;

map<node,bool> use;

int ans,n;

char s[120];

int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    for (int a=1;a<=n;a++)
    {
        scanf("%s",s+1);
        int l=strlen(s+1);
        memset(now.z,0,sizeof(now.z));
        for (int a=1;a<=l;a++)
            now.z[s[a]-'A'+1]++;
        if (!use[now]) use[now]=true,ans++;
    }
    printf("%d
",ans);
    
    return 0;
}
std

设分成的每段长为Li,g=gcd(Li)

那么一共有n/g 个单位

设f[g]表示以g为周长,且三边gcd为1 的三角形的个数

设h[n/g]表示把n/g个单位 分配给任意多个三角形的个数

那么 ans=Σ f[g]*h[n/g]   (g|n)

求f[g]:

设g=a+b+c,且a<=b<=c

对b和c的大小分两种情况讨论:

① b==c :

==> g=a+2b,那么b∈[ceil(g/3),floor((g-1)/2)]

所以f[g]=floor((g-1)/2)- ceil(g/3) +1

② b<c :

a,b,c 的每一种方案都可以看做由 a,b,c-1的每一种方案转移过来

但有一种除外:a+b=c,因为此时a,b,c-1 合法,a,b,c 不合法

当g为偶数时,a+b+a+b=g,g=2*(a+b),所以有floor(g/4)个

所以f[g]=f[g-1]+ (b&1)? 0 : -g/4

然后因为要求三边长互质,所以枚举g的每个因数k,f[g]-=f[k]

求h[i]:

把i个物品分成任意份的方案数=C(i-1,0)+C(i-1,1)+……+C(i-1,i-1)

= 2^(i-1)

#include<cmath>
#include<cstdio>
#include<algorithm>

#define N 1000001
const int mod=1e9+7;

using namespace std;

int f[N],g[N];
int divisor[N],cnt;

void ADD(int &a,int b) { a+=b; a>=mod ? a-=mod : 0; }

int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    int n; scanf("%d",&n);
    for(int a=3;a<=n;a++) 
    {
        f[a]=f[a-1]; ADD(f[a],(a-1>>1)-ceil(a*1.0/3)+1);
        ADD(f[a],(a&1) ? 0 : -a/4);
    }
    for(int a=1;a*a<=n;++a)
        if(n%a==0)
        {
            divisor[++cnt]=a;
            if(a*a!=n) divisor[++cnt]=n/a;
        }
    sort(divisor+1,divisor+cnt+1);
    for(int i=1;i<=cnt;i++)
        for(int j=1;j<i;j++) 
            if(divisor[i]%divisor[j]==0) ADD(f[divisor[i]],mod-f[divisor[j]]);
    g[0]=1;
    for(int i=1;i<=n;i++) g[i]=g[i-1],ADD(g[i],g[i-1]);
    int ans=0;
    for(int i=1;i<=cnt;i++) ADD(ans,1ll*f[divisor[i]]*g[n/divisor[i]-1]%mod);
    printf("%d",ans);
}
View Code

考场上WW的组合数,得了30,挂了30

#include<cstdio>
using namespace std;
int n;
int one[1001];
int C[1001][1001];
const int mod=1e9+7;
bool check(int i,int j,int k)
{
    if(!i || !j || !k) return false;
    if(!((i<=j)&&(j<=k))) return false;
    if(i+j<=k) return false;
    if(j-i>=k) return false;
    return true;
}
void pre()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            for(int k=j;k<=i;k++)
                if(check(j,i-j-k,k)) one[i]++;
    for(int i=0;i<=n;i++) C[i][0]=1;
    for(int i=0;i<=n;i++) C[0][i]=1;
    for(int i=2;i<=n;i++)
        for(int j=1;j<=i;j++)
            C[i][j]+=C[i-1][j-1]+C[i-1][j],C[i][j]%=mod;
    int tot,ans=0;
    for(int i=3;i<=n;i++)
        if(n%i==0)
        {
            tot=n/i;
            tot--;
            if(!tot) { ans+=one[i]; continue;}
             for(int j=1;j<=tot;j++) 
     
              ans=(ans+1ll*one[i]*C[tot][j-1]*j)%mod;
            
             
        }
    printf("%d",ans);
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%d",&n);
    pre();
}
View Code

 这题竟然是个容斥原理

解决本题的关键:行交换、列交换对答案不影响

将左视图按从下往上递减,正视图从左往右递减排列

那整张图的高度从左下到右上呈阶梯状递减

这样所有高度相同的呈现倒‘L’形,如下图所示蓝色部分

如果我们按高度递减的顺序依次计算每个倒‘L’形的方案数,那么这些倒‘L’形相对独立

答案就是所有倒‘L’形答案的乘积

 

如何计算单个倒‘L’形的答案?——容斥原理

假设上图为已经按高度排好序的图

设当前正在处理高度为h的倒‘L’形

令nn表示当前有nn行的左视图高度为h,mm表示当前有mm列的主视图高度为h

n表示当前有n行的左视图高度>=h,m表示当前有m列的主视图高度>=h

定义性质pk表示 在这nn行mm列中,有k行/列不满足看到的高度为h

那根据容斥原理,

不具有任何一个性质p的方案和=

全集-Σ|pi|+Σ|pi∩pj|-Σ|pi∩pj∩pk|+…+(-1)^m*|p1∩p2∩…∩pm|

也就是所有方案-所有1行/列不满足条件的方案+所有2行/列不满足条件的方案-……

如何求有k行/列不满足条件的方案数?

设现在要求在倒‘L’形中,有i行j列不满足条件的方案数A,i+j=k

那么A分为两部分

① i行j列不能满足条件的部分:

当前高度为h,不能满足条件,每一个各自可以填[0,h-1],每个格子有h种方案

所以此时方案数=h^ (n*m-(n-i)*(m-j))

② 倒‘L’形中其他位置可以任意填的部分

 当前高度为h,任意填就是可以填[0,h],每个各自有h+1种方案

所以此时的方案数=(h+1)^((n-i)*(m-j)-(n-nn)*(m-mm))

这是选定i行j列,所以还要乘上在nn行中选i行,在mm列中选j列的方案

终上所述,每个倒‘L’形 的答案为 (-1)^(i+j)* C(nn,i)* C(mm,j)* h^ (n*m-(n-i)*(m-j)) * (h+1)^((n-i)*(m-j)-(n-nn)*(m-mm))

#include<cstdio>
#include<algorithm>

using namespace std;

typedef long long LL;

#define N 51
#define H 10001
const int mod=1e9+9;

int a[H],b[H];
int n,m;
int C[N][N];

void pre(int k)
{
    for(int i=0;i<=k;i++) C[i][0]=1;
    for(int i=1;i<=k;i++)
        for(int j=1;j<=i;j++) 
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; 
}

int pow(int a,int b)
{
    int res=1;
    for(;b;b>>=1,a=1ll*a*a%mod)
        if(b&1) res=1ll*res*a%mod;
    return res;
}

int cal(int n,int m,int nn,int mm,int h)
{
    int res=0,tmp;
    for(int i=0;i<=nn;++i)
        for(int j=0;j<=mm;++j)
        {
            tmp=1ll*pow(h,n*m-(n-i)*(m-j))*pow(h+1,(n-i)*(m-j)-(n-nn)*(m-mm))%mod*C[nn][i]%mod*C[mm][j]%mod;
            if((i+j)&1) res=((res-tmp)%mod+mod)%mod;
            else res+=tmp,res%=mod;    
        }
    return res;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    int x;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i) scanf("%d",&x),a[x]++;
    for(int i=1;i<=m;++i) scanf("%d",&x),b[x]++;
    pre(max(n,m));
    LL res=1;
    int nown=0,nowm=0;
    for(int i=10000;i>=0;i--)
        if(a[i] || b[i])
        {
            nown+=a[i]; nowm+=b[i];
            res=1ll*res*cal(nown,nowm,a[i],b[i],i)%mod;
        }
    printf("%I64d",res);
}
View Code
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/7640451.html