您的位置:首页 > 其它

noip模拟题11.16 距noip2016还剩两天

2016-11-16 20:01 274 查看

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
[k]^T[i],dp
[k]即为答案。其中T[i]表示m列中共有多少个kn+i列,然后用快速幂预处理C
[p]^T[i]。

C表示组合数,C[i][j] 意为在 i 个数中选 j 个的方案数。由于当前已经推到最后一个方块了,所以前面的方案数为C
[k]^T[i]种。

值得注意的是,我们将n×m的方块弄成n×n的方块排列在一起后,最有一个方块有可能不是n×n的,所以C
[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
[i],num);//0表示有空缺部分
val[1][i]=pow(C
[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
[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(
b911
),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,我真是越做越慌……太吓人了……⊙﹏⊙
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  模拟题