[NOIP模拟题][数学][DP][逆元][快速幂][暴力优化]

2 篇文章 0 订阅
1 篇文章 0 订阅

T1

LGTB 与序列
LGTB 得到了一个序列,他想在这个序列中选择一个最长的连续子序列,使得这个子序列的最大公约数等于
1。请告诉他他能得到的最大长度,如果没有这样的序列,输出��1
输入
输入第一行包含一个整数n 代表序列大小
接下来一行,包含n 个整数a1, a2, …, an,代表序列
对于50% 的数据,1 n 1000
对于100% 的数据,1 n 105 1 ai 109
输出
输出包含一个整数l,代表最长的连续子序列,如果无解请输出��1
样例
样例输入样例输出
27
2
2
样例输入样例输出
32
2 4
-1
2

显然的,最终要么为n要么为-1
考虑到如果答案不为n,那么必然每一个数都存在一个相同的质因数,那我们只须选取A[1]逐一与其它数检验GCD即可

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#include<set>
#define LL long long 
using namespace std;
const int maxn=1e5+5;
int n,A[maxn],ans=-1;
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=x*10+ch-'0'; ch=getchar();}
    return x;
}
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    n=readint();
    for (int i=1;i<=n;i++) A[i]=readint();
    if (n==1&&A[1]==1) ans=1;
    for (int i=2;i<=n;i++) if (gcd(A[1],A[i])==1)
    {
        ans=n;
        break;
    }
    printf("%d",ans);
    return 0;
}

T2

LGTB 与桌子
LGTB 新买了一张n m 的矩(桌) 阵(子),他想给某些1 1 的小矩形染色,使得染色之后,原矩阵的每
个n n 的子矩阵中都包含恰好k 个被染色了的小矩形
他想知道有多少种染色方案能让他满足上述要求
因为答案可能很大,请输出方案数模1000000007 (109 + 7) 后的值
输入
输入第一行包含三个整数n, m, k 意义如题面所示
对于15% 的数据,1 n m 20, n m
对于40% 的数据,1 n 10, n m 1000
对于100% 的数据,1 n 100, n m 1018, 0 k n2
输出
输出包含1 个整数,代表LGTB 的方案数
样例
样例输入样例输出
5 6 1 45

好神的递推,表示考试的时候我真的想不出来
有一个容易发现的性质,第1列填的总数必须等等第n+1列填的总数,那么这个性质可以推广为sum[i]=sum[i+kn],那我们就可以把一块长为n的n*n矩阵单独拿出来分块处理
然后dp方程就不难推出了
dp[i][j]表示所有块的前i个位置都处理完了,1-i用了j个数
dp[i][j]=sigma(dp[i-1][j-p]*C(n,p)*T[i])(T[i]表示i这个位置有多少)
快速幂部分预处理出来即可
真是醉了,评测的时候我数组开小了,cena上显的是WA,搞得我调了半个小时,还浪费了ZTY半个小时

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else 
#define AUTO "%lld"
#endif
using namespace std;
const int N=100,M=1e4+5;
const int mod=1e9+7;
typedef long long LL;
int n,kind;
LL m,C[N+5],dp[N+5][M],T[N+5],pow[N+5][N+5];
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=getchar();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if (!b) d=a,x=1,y=0;
    else gcd(b,a%b,d,y,x),y-=x*(a/b);
}
LL inv(LL a)
{
    LL d,x,y;
    gcd(a,mod,d,x,y);
    return d==1?(x+mod)%mod:-1;
}
int qpow(LL a,LL p)
{
    LL ret=1;
    while (p)
    {
        if (p&1) ret=ret*a%mod;
        a=a*a%mod;
        p>>=1;
    }
    return ret;
}
void init()
{
    LL temp=m/n;
    for (int i=1;i<=n;i++) T[i]=temp;
    temp=m%n;
    for (int i=1;i<=temp;i++) T[i]++;
    C[0]=1;
    for (int i=0;i<n;i++) C[i+1]=(n-i)*inv(i+1)%mod*C[i]%mod;
    for (int i=0;i<=n;i++) dp[i][0]=1;
    for (int i=0;i<=n;i++)
      for (int j=0;j<=n;j++)
        pow[i][j]=qpow(C[i],T[j]);
    //for (int i=0;i<=n;i++) 
      //dp[1][i]=pow[i][1];

} 
int main()
{
    freopen("table.in","r",stdin);
    freopen("table.out","w",stdout);
    n=readint(); scanf(AUTO,&m); kind=readint();
    init();
    for (int i=1;i<=n;i++)
      for (int j=1;j<=kind&&j<=i*n;j++)
        for (int k=0;k<=j&&k<=n;k++)
          dp[i][j]=(dp[i][j]+(dp[i-1][j-k]*pow[k][i]%mod))%mod;
    printf(AUTO,dp[n][kind]);
    return 0;
}

就一个·大暴力题,我打的暴力没注意常数,所以只有60分
100分的优化很神奇
普通的暴力是枚举每一条链上的点,但当某一条链很长时,这个复杂度就会特别大,那就可以把链分成两部分,一种是点数小于logn的短链,一种是点数大于logn的长链,短链做法同上,长链我们就是n^2枚举每一条链来暴力,但因为长链不超过logn条,所以复杂度可以接受
说白了,标算就是把两种暴力打在了一起

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cctype>
#include<vector>
using namespace std;
const int flag=400;
const int N=1e5;
int ans,n,size1,size2,few[N+1],great[N+1],cnt1,cnt2;
struct Point
{
    int X,Y;
    bool operator<(const Point &temp)const 
    {
        return X<temp.X||(X==temp.X&&Y<temp.Y);
    }
};
set<Point>table;
vector<int>chain[N+1];
int readint()
{
    int x=0; char ch=getchar();
    while (!isdigit(ch)) ch=readint();
    while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    return x;
}
int main()
{
    freopen("square.in","r",stdin);
    freopen("square.out","w",stdout);
    n=readint();
    for (int i=1;i<=n;i++)
    {
        int X=readint(),Y=readint();
        table.insert((Point){X,Y}); chain[X].push_back(Y);
    }
    for (int i=0;i<=N;i++)
    {
        int siz=chain[i].size();
        if (!siz) continue;
        sort(chain[i].begin(),chain[i].end());
        if (siz<flag) few[++size1]=i;
        else great[++size2]=i; 
    }
    for (int i=1;i<=size1;i++)
    {
        int ID=few[i],siz=chain[ID].size(); //ID为链的编号 
        int cur=0;
        while (cur<siz)
        {
            int nxt=cur+1;
            while(nxt<siz)
            {
                int Y1=chain[ID][cur],Y2=chain[ID][nxt];
                int aim=ID+Y2-Y1;
                if (table.count((Point){aim,Y1})&&table.count((Point){aim,Y2})) ans++;
                aim=ID-(Y2-Y1);
                nxt++;
                if (aim<0) continue;
                if (chain[aim].size()>=flag)
                  if (table.count((Point){aim,Y1})&&table.count((Point){aim,Y2})) ans++;
            }
            cur++;
        }
    }
    for (int i=1;i<size2;i++)
      for (int j=i+1;j<=size2;j++)
      {
        int ID1=great[i],ID2=great[j];
        int siz1=chain[ID1].size(),siz2=chain[ID2].size();
        int L=0,R=0;
        while (L<siz1&&R<siz2)
        {
            while (chain[ID1][L]^chain[ID2][R])
            {
              if (L<R) L++;
              else R++;
              if (L==siz1||R==siz2) break;
            }
            if (L==siz1||R==siz2) break;
            int aim=chain[ID1][L]+(ID2-ID1+1);
            if (table.count((Point){ID1,aim})&&table.count((Point){ID2,aim})) ans++;
        }
      }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值