3.25-3.28日志及3.26模拟赛分析

3.25:今天早上做的还好,做了4题洛谷和1题poj,下午则做的不太多,只有一题poj。
3.26:今天在百忙之中(模拟赛),还是刷了一道poj的。模拟赛终于稳定了名次,Rank.7左右,恢复了之前的状态,希望能坚持,甚至做的更好。结尾追加题目与题解。
3.28:今天JPY讲了KMP,Trie和AC自动机,还是要多做题,搞懂它们。刷了poj7题,hdu1题。


3.26模拟赛题目&题解:
T1
题目大意:有n头牛,2头公牛中间至少要有k头母牛。问方案数对大质数取模值。(n<=100000)。
我的想法:不造为什么n这么小,按理说n为10000000都没问题的。其实吧,这题是一个不难的DP。数组f[]表示放到第i头牛的方案数,为了区分公牛母牛,我们设数组f[][0]来表示公牛,f[][1]来表示母牛,母牛由于没有限制条件,所以某一位的值为前一位公牛母牛的和,而公牛由于有限制所以只有n-k+1的地方可以有公牛出现,所以公牛某一位的值为n-k+1位公牛母牛个数和,最后答案即为f[n][1],f[n][0]相加,注意初始化f[i][1],f[i][0]都是1,1~k+1的公牛值是1。
则有:

f[i][x] i=1 2 3 4 5···
   x=0    1 1 1 2 3
   x=1    1 2 3 4 6
   ans    2 3 4 6 9

其实可以优化到去掉后一维[x],但x小,且加上易于理解,就不去了,orz我的同学还有线段树,排列组合AC的。
T2
题目大意:有n组数,每一组有d个数,你最多可以用不超过k个任意数,请你找出你能表示的最多的连续组。(T组数据,T<=10,n<=1000000,d=1~3,k<=4)
我的想法:唉,orz爆〇了,其实写暴力可以得到30分优化的好可以60分+,但由于种种原因,我写wei了。。。讲一下标算。这个n较大n,但好像没有O(n)算法,只能退而求其次,O(n*lgn)成了首选,我们二分组数mid,从mid向两边尽可能的拓展,不断更新答案,为了避免TLE,我们当r-l已经小于ans时直接退出。
部分代码:

bool check(int x)
{
    for(int i=1;i<=d;i++)if(mark[a[x][i]])return 1;
    return 0;
}
void dfs(int l,int r,int lx,int rx,int k)
{
    while(check(l-1)&&l>lx)l--;
    while(check(r+1)&&r<rx)r++;
    if(r-l>ansr-ansl)ansl=l,ansr=r;
    if(r-l==ansr-ansl&&l<ansl)ansl=l,ansr=r;
    if(k==K)return;
    for(int i=1;i<=d;i++)
    {
    if(l!=lx)
    {
    mark[a[l-1][i]]=1;
    dfs(l-1,r,lx,rx,k+1);
    mark[a[l-1][i]]=0;
    }
    if(r!=rx)
    {
    mark[a[r+1][i]]=1;
    dfs(l,r+1,lx,rx,k+1);
    mark[a[r+1][i]]=0;
    }
    }
}
void solve(int l,int r)
{
    if(r-l<ansr-ansl)return;
    if(l>r)return;
    int mid=(l+r)>>1;
    for(int i=1;i<=d;i++)
    {
    mark[a[mid][i]]=1;
    dfs(mid,mid,l,r,1);
    mark[a[mid][i]]=0;
    }
    solve(l,mid-1);solve(mid+1,r);
}

T3
题目大意:给你一个数x,求1~x中有多少数是可以被自己每一位上的数整除的。(多组数据,数据组数少于不多于10000,x<=9*10^18)
我的想法:嘿嘿嘿。。。我选择暴力50%。显然的是数位DP,但比较难构造,懒得分析了,233。
大致代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <queue>
#include <algorithm>
using namespace std;
#define LL long long
LL dp[20][2525][55];
int digit[20];
int hash[2525];
int gcd(int a,int b)
{
   if(b == 0) return a;
   return gcd(b,a%b);
}
int calc_lcm(int a,int b)
{
   return a/gcd(a,b)*b;
}
LL dfs(int pos,int mod,int lcm,bool limit)
{
   LL ans = 0;
   if(pos<=0) return mod % lcm == 0;
   if(!limit && dp[pos][mod][hash[lcm]]!=-1) return dp[pos][mod][hash[lcm]];
   int end = limit ? digit[pos] : 9;
   for(int i=0;i<=end;i++)
   {
      ans += dfs(pos-1,(mod*10+i)%2520,i?calc_lcm(lcm,i):lcm,limit && (i==end));
   }
   if(!limit) dp[pos][mod][hash[lcm]] = ans;
   return ans;
}
LL calc(LL a)
{
   if(a<0) return 0;
   int len = 0;
   while(a>0)
   {
      digit[++len] = a%10;
      a/=10;
   }
   LL ans = dfs(len,0,1,1);
   return ans;
}
void init()
{
   memset(dp,-1,sizeof(dp));
   int id = 0;
   for(int i=1;i*i<=2520;i++)
   {
      if(2520%i == 0)
      {
         hash[i] = id++;
         if(i*i!=2520) hash[2520/i] = id++;
      }
   }
}
int main()
{
   freopen("flower.in","r",stdin);
   freopen("flower.out","w",stdout); 
   init();
   int t;
   LL x,y;
   scanf("%I64d",&x);
   while(scanf("%I64d",&x)!=EOF)
   {
      printf("%I64d\n",calc(x));
   }
   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值