noip模拟题11.16 距noip2016还剩两天

T1 LGTB 与序列

LGTB 得到了一个序列,他想在这个序列中选择一个最长的连续子序列,使得这个子序列的最大公约数等于1。请告诉他他能得到的最大长度,如果没有这样的序列,输出-1

输入

输入第一行包含一个整数n 代表序列大小
接下来一行,包含n 个整数a1, a2, …, an,代表序列
对于50% 的数据,1<n<1000
对于100% 的数据,1<n<10^5, 1<ai<10^9

输出

输出包含一个整数l,代表最长的连续子序列,如果无解请输出-1。

样例

样例输入1
2
7 2
样例输出1
2
样例输入2
3
2 2 4
样例输出2
-1

这题乍一看很恼火,考试时花了点时间找一串序列需要剔除某些数的情况,但我找不到…然后又花了点时间打了个素数表…然后…惊奇地发现…这题太坑了,只要找整个序列的gcd,如果gcd==1,就输出 n,否则输出-1。因为一个序列不满足的话肯定所有数都有一个公约数,所以只要两两求gcd就行…
代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long que[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline long long gcd(long long a,long long b)
{
    if(b==0)return a;
    else return gcd(b,a%b);
}
int main()
{
    freopen("seq.in","r",stdin);
    freopen("seq.out","w",stdout);
    int n=read();

    for(int i=1;i<=n;++i)
        que[i]=read();
    if(n==1)
    {
        if(que[1]==1)
            printf("1");
        else printf("-1");
        return 0;
    }
    long long tmp=que[1];
    for(int i=2;i<=n;i++)
    {
        tmp=gcd(tmp,que[i]);
        if(tmp==1)
        {
            printf("%d",n);
            return 0;
        }
    }
    printf("-1");
    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<10^18, 0<k<n^2

输出

输出包含1 个整数,代表LGTB 的方案数

样例

样例输入1
5 6 1
样例输出1
45

样例说明

这里写图片描述
样例如图所示,如果在灰色区域染一个格子,有20 种方案。如果在两边的白色格子各染一个格子,有25 种方案。共45 种方案。

表示这种需要找规律的dp题我真的是无能为力…考试时码了个暴力交上去得了20分…
不难得到这个性质:当前n列有k个染色的时候,假设第1列有x个染色,那么第n+1列也应该有x个染色,则kn+i列的染色个数与第i列染色个数相同。
那么我们可以设数组 dp[i][j] 表示一个 i×n 的矩阵中涂 j 个方块的方案数。因为 m 很大,我们可以不必将所有矩阵求出来,dp[i+1][j+k]=∑dp[i][j]×C[n][k]^T[i],dp[n][k]即为答案。其中T[i]表示m列中共有多少个kn+i列,然后用快速幂预处理C[n][p]^T[i]。
C表示组合数,C[i][j] 意为在 i 个数中选 j 个的方案数。由于当前已经推到最后一个方块了,所以前面的方案数为C[n][k]^T[i]种。
值得注意的是,我们将n×m的方块弄成n×n的方块排列在一起后,最有一个方块有可能不是n×n的,所以C[n][k]^T[i]中的T[i]有可能不一样,那么在快速幂求解的时候,不空缺的部分要求^((m/n)+1),空缺部分要求^(m/n)。
代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
const int mod=1e9+7;
long long dp[105][10005],C[105][105];
long long val[2][105];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline long long pow(long long tmp,long long k)
{
    if(k==1)return tmp%mod;
    if((k&1)==1)
    {
        long long op=pow(tmp,k>>1)%mod;
        return (((op*op)%mod)*tmp)%mod;
    }
    else
    {
        long long op=pow(tmp,k>>1)%mod;
        return (op*op)%mod;
    }
}
int main()
{
    freopen("table.in","r",stdin);
    freopen("table.out","w",stdout);
    int n=read();long long m;scanf(AUTO,&m);int k=read();
    for(int i=0;i<=n+3;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }//杨辉三角形求组合数
    dp[0][0]=1;
    long long num=m/n;//一共有多少个方块
    long long le=m%n;//多出来无法构成n×n方块的序列
    for(int i=0;i<=n;i++)
    {
        val[0][i]=pow(C[n][i],num);//0表示有空缺部分
        val[1][i]=pow(C[n][i],num+1);//1表示无空缺部分
    }
    for(int i=0;i<n;i++)//直接处理最后一个也许不完整的方块
        for(int j=0;j<=i*n;j++)
            for(int op=0;op<=n;op++)
            {
                long long tmp=i<le?val[1][op]:val[0][op];
                //此处是i<le,因为我们是从i推i+1
                (dp[i+1][j+op]+=(dp[i][j]*tmp)%mod)%=mod;
            }//新加一列,在新加一列中涂op个小方块
    printf(AUTO,dp[n][k]);
    return 0;
}

T3 LGTB 与正方形

LGTB 最近迷上了正方形,现在他有n 个在二维平面上的点,请你告诉他在这些点中选4 个点能组成四条边都平行于坐标轴的正方形的方案数有多少

输入

输入第一行包含一个整数n,代表点的数量
接下来n 行每行包含两个整数xi, yi,代表点的坐标
对于10% 的数据,1<n<50
对于30% 的数据,1<n<1000
对于100% 的数据,1<n<10^5,0<xi, yi<10^5
数据保证没有两点在同一位置

输出

输出包含一个整数代表方案数

样例

样例输入1
5
0 0
0 2
2 0
2 2
1 1
样例输出1
1
样例输入2
9
0 0
1 1
2 2
0 1
1 0
0 2
2 0
1 2
2 1
样例输出2
5

直接暴力肯定是要T的。
考虑优化:用集合储存每个横坐标,再将每个横坐标上的点存下来。接下来有两种暴力方法:
1:枚举每个点,再枚举这个点上面的点(也就是横坐标相同,纵坐标大于这个点的点),判断右边是否有两个点和这两个点的纵坐标相同,且横坐标之差等于纵坐标之差。
2:枚举每个横坐标,再枚举大于这个横坐标的横坐标,从第一个开始向上,如果第一个的纵坐标小了,指针1++,大了同理,若相等,判断两个横坐标上面是否有高度为两个横坐标之差点。
数组存不下,用集合。以上方法可以过60%。
正解:将横坐标分为两组:点数大于400的分为长边,小于400的分为短边,短边用方法1处理,但同时还要判断左边的长边是否有满足的情况。长边用方法2处理。用集合处理过70%,hash处理可以AC,但我不会……(废人)。
70%代码:

#include<set>
#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
struct node{
    int x,y;
    bool operator<(const node&a)const
    {
        if(a.x!=x)return a.x>x;
        else return a.y>y;
    }
}gg[100005];
int small[100005],big[100005],low;
int high,bb[100005],vis[100005];
set<node>judge;
vector<int>ma[100005];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    freopen("square.in","r",stdin);
    freopen("square.out","w",stdout);
    int n=read(),maxn=0;
    for(int i=1;i<=n;i++)
    {
        gg[i].x=read();gg[i].y=read();
        ma[gg[i].x].push_back(gg[i].y);
        judge.insert(gg[i]);
        maxn=max(maxn,gg[i].x);
        vis[gg[i].x]=1;
    }
    for(int i=0;i<=maxn;i++)
        if(vis[i])
        {
            int num=ma[i].size();
            if(num<400)
                small[++low]=i;
            else
            {
                big[++high]=i;
                bb[i]=1;
            }
            sort(ma[i].begin(),ma[i].end());
        }
    int ans=0;
    for(int i=1;i<=low;i++)
        for(int j=0;j<ma[small[i]].size();j++)
            for(int k=j+1;k<ma[small[i]].size();k++)
            {
                int tmp=small[i]-(ma[small[i]][k]-ma[small[i]][j]);
                if(tmp)
                    if(judge.count((node){tmp,ma[small[i]][j]}))
                        if(judge.count((node){tmp,ma[small[i]][k]}))
                            if(bb[tmp]==1)
                                ans++;
                tmp=small[i]+(ma[small[i]][k]-ma[small[i]][j]);
                if(tmp<=maxn)
                    if(judge.count((node){tmp,ma[small[i]][j]}))
                        if(judge.count((node){tmp,ma[small[i]][k]}))
                            ans++;
            }
    for(int i=1;i<=high;i++)
        for(int j=i+1;j<=high;j++)
        {
            int op1=0,op2=0;
            while(op1<ma[big[i]].size()&&op2<ma[big[j]].size())
            {
                if(ma[big[i]][op1]>ma[big[j]][op2])
                    op2++;
                else if(ma[big[i]][op1]<ma[big[j]][op2])
                    op1++;
                else
                {
                    int tmp=big[j]-big[i];
                    if(judge.count((node){big[i],ma[big[i]][op1]+tmp}))
                        if(judge.count((node){big[j],ma[big[j]][op2]+tmp}))
                            ans++;
                    op1++;op2++;
                }
            }
        }
    printf("%d",ans);
    return 0;
}

表示最近的题都很阴阳怪气,临近noip,我真是越做越慌……太吓人了……⊙﹏⊙

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值