Bestcoder2017.4.1瞎打

A

贪心一段一段选,每次越大越好。
证明?
设f[i]表示前i个至少多少下。
f显然有单调性。
那么对于一个i找到一个最大的j,然后f[i]=f[i-j]+1
这样看看就是贪心啊。

#include<cstdio>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
map<int,bool> b;
int f[maxn],left[maxn],a[maxn];
int i,j,k,l,t,n,m,ans,ca;
int main(){
    scanf("%d",&ca);
    while (ca--){
        scanf("%d",&n);
        fo(i,1,n) scanf("%d",&a[i]);
        b.clear();
        i=n;
        ans=0;
        while (i){
            j=i;
            b[a[i]]=1;
            while (j>1&&!b[a[j-1]]){
                b[a[j-1]]=1;
                j--;
            }
            fo(k,j,i) b[a[k]]=0;
            ans++;
            i=j-1;
        }
        printf("%d\n",ans);
    }
}

B

枚举开头在哪里,这样前导0比较容易判。
考虑判定cnt0个0,cnt1个1,cnt2个2,能否恰好取k个和模3为v。
0没有影响,可以考虑最大化cnt1和cnt2的选取个数,使和模3为0,然后看看剩余需要的是否<=cnt0。
v=1的话对于取1个1和2个2都试试
v=2的话对于取2个1和1个2都试试
于是问题变成使和模3为0
取1个1和1个2不变
3个1或3个2也不变
前者假如做了k次,我们可以看做做了k%3次,接下来都用后者表示。
那么对于前者我们可以只做0~2次。
剩下都是后者,能取尽量取。
然后这题结束了吗?
注意一个0不算前导0,特判掉即可。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=100000+10;
int a[maxn];
int i,j,k,l,t,v,n,m,ca,cnt0,cnt1,cnt2;
bool czy;
int read(){
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    return ch-'0';
}
int work(int x,int y,int k){
    if (!k) return 0;
    int i,l=0,kk,xx,yy,ans=k;
    fo(i,0,2){
        kk=k;xx=x;yy=y;
    xx-=i;yy-=i;
    if (xx<0||yy<0) break;
    kk-=2*i;
    if (kk<0) break;
    t=min(kk/3,xx/3);
    kk-=3*t;
    t=min(kk/3,yy/3);
    kk-=3*t;
    ans=min(ans,kk);
    }
    return ans;
}
int main(){
    scanf("%d",&ca);
    while (ca--){
        scanf("%d%d",&n,&m);
        cnt0=cnt1=cnt2=v=0;
        fo(i,1,n){
            a[i]=read();
            (v+=a[i])%=3;
            if (a[i]%3==0) cnt0++;
            else if (a[i]%3==1) cnt1++;
            else cnt2++;
        }
        czy=0;
        fo(i,1,m+1){
            if (i>1) (v-=a[i-1])%=3;
            (v+=3)%=3;
            if (a[i]%3==0) cnt0--;
            else if (a[i]%3==1) cnt1--;
            else cnt2--;
            if (a[i]==0) continue;
            if (v==0){
                if (work(cnt1,cnt2,m-i+1)<=cnt0){
                    czy=1;
                    break;
                }
            }
            else if (v==1){
                if (cnt1&&m-i>=0&&work(cnt1-1,cnt2,m-i)<=cnt0){
                    czy=1;
                    break;
                }
                if (cnt2>=2&&m-i-1>=0&&work(cnt1,cnt2-2,m-i-1)<=cnt0){
                    czy=1;
                    break;
                }
            }
            else if (v==2){
                if (cnt1>=2&&m-i-1>=0&&work(cnt1-2,cnt2,m-i-1)<=cnt0){
                    czy=1;
                    break;
                }
                if (cnt2&&m-i>=0&&work(cnt1,cnt2-1,m-i)<=cnt0){
                    czy=1;
                    break;
                }
            }
        }
        if (m==n-1){
            fo(i,1,n)
                if (!a[i]){
                    czy=1;
                    break;
                }
        }
        if (czy) printf("yes\n");else printf("no\n");
    }
}

C

设一下f[i]表示长度为n的序列恰好只出现i种数有多少这样的序列。
考虑容斥有
f[i]=ini1j=1f[j]Cji
对于长度相同的环缩成一起。
一共最多只有6种不同长度的环。
枚举每种环取多少个。
假如一个有k个节点的环取了j个,乘个组合数。
把所有取了至少一个的环求lcm。
最后如果一共取了z个点还要乘f[z]
就是这样。

#include<cstdio> 
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int mo=1000000007;
int a[7],c[7],b[30],bz[30],f[30];
int C[30][30];
bool pd[30];
int i,j,k,l,t,n,m,top,ca,ans;
char ch;
char get(){
    char ch=getchar();
    while (ch<'a'||ch>'z') ch=getchar();
    return ch;
}
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int lcm(int a,int b){
    return a*b/gcd(a,b) ;
}
int quicksortmi(int x,int y){
    if (!y) return 1;
    int t=quicksortmi(x,y/2);
    t=(ll)t*t%mo;
    if (y%2) t=(ll)t*x%mo;
    return t;
}
/*void dfs(int x,int y,int z,int f){
    if (x==top+1){
        (ans+=(ll)f*quicksortmi(z,n)%mo*y%mo)%=mo;
        return;
    }
    dfs(x+1,y,z,f);
    dfs(x+1,lcm(y,c[x]),z+a[x],-f);
}*/
void dfs(int x,int y,int z,int v){
    if (x==top+1){
        (ans+=(ll)y*f[z]%mo*v%mo)%=mo;
        return;
    }
    int i;
    fo(i,0,a[x]){
        dfs(x+1,i?lcm(y,c[x]):y,z+i,(ll)v*C[a[x]][i]%mo);
    }
}
int main(){
    C[0][0]=1;
    fo(i,1,26){
        C[i][0]=1;
        fo(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo;
    }
    scanf("%d",&ca);
    while (ca--){
        scanf("%d",&n);
        f[1]=1;
        fo(i,2,26){
            f[i]=quicksortmi(i,n);
            fo(j,1,i-1) (f[i]-=(ll)f[j]*C[i][j]%mo)%=mo;
        }
        fo(i,1,26){
            ch=get();
            b[i]=ch-'a'+1;
        }
        fo(i,1,26) pd[i]=bz[i]=0;
        top=0;
        fo(i,1,26)
            if (!pd[i]){
                k=b[i];
                pd[i]=1;
                t=1;
                while (k!=i){
                    t++;
                    pd[k]=1;
                    k=b[k];
                }
                if (!bz[t]){
                    bz[t]=++top;
                    c[top]=a[top]=t;
                }
                else a[bz[t]]+=t;
            }
        ans=0;
        dfs(1,1,0,1);
        (ans+=mo)%=mo;
        printf("%d\n",ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值