暑假2019培训:Day7提高组测试赛
概述
今天倒是真的像cdcq的欢乐赛啊~
来看看今天的成绩~:
我们的std cdcq这是怎么了???
不过倒是比我出色(不比我出色重读初中吧)
一共70人这次考试。。。
好,让我们来看看今天的题目
题目顺序
- 1.PCR
- 2.排列
- 3.新数独
1.PCR
(pcr.pas/c/cpp)
【背景】
PCR(多聚酶链式反应)技术是一种机器自动控制的人工dna复制技术。
【问题描述】
该技术中根据生成dna两条链的长度可以将生成的dna分成长中、中短、短短三种类型。
由图示可以看出,第一次循环的时候模板链会产生两条长中型dna,从第二次循环开始,每条长中型dna可以生成长中和中短型dna各一条,每条中短型dna可以生成中短和短短型dna各一条,而每条短短型dna可以生成两条短短型dna。
在pcr生成的所有dna产物中,只有短短型dna是我们需要的目的基因,现在问你,pcr技术进行n次循环后会产生多少目的基因?
因为答案可能很大,你只需要输出目的基因数%19260817的结果
【输入】
一行一个整数n
【输出】
一行一个整数表示目的基因数对大整数取模的结果
【输入输出样例1】
p c r . i n pcr.in pcr.in
4
p c r . o u t pcr.out pcr.out
8
【数据范围】
有 10%的数据:n=2
有 10%的数据:n=3
有 10%的数据:n=5
对于 70%的数据:n<=106
对于 100%的数据:n<=1018
============================================
分析
天哪,好良心的暴力
n=2,n=3,这可就是20分了,然而这个图上就有,只要扫一下就行了~
那这题那四位暴零的,确实说不过了,好像……
这题题面很长
根据出题者cdcq说:
嗯嗯,这很好~
拿来看看这道题的意思吧~
已知 a[n]=2,b[n]=a[n-1]+b[n-1],c[n]=b[n-1]+2*c[n-1]且 a[1]=2,b[1]=c[1]=0。
给你一个整数 n,你要输出 c[n]%19260817 的结果
那这道题的暴力50分代码,就一目了然,很好写了~
代码开场秒掉:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
const int MOD=19260817;
int n;
long long x,y,ans=0;
int main()
{
freopen("pcr.in","r",stdin);
freopen("pcr.out","w",stdout);
int i;
for(scanf("%d",&n),x=ans=0,i=2;++i<=n;)
{
x=(x+2)%MOD;
ans=(x+2*ans%MOD)%MOD;
}
printf("%lld",ans%MOD);
return 0;
}
那满分就要找规律了
先用暴力打出前面几个式子的答案
1 2 3 4 5 6 7
0 0 2 8 22 52 114
好,1没有这个数据,2打表
我们从3开始看
对于每个式子/2
3 4 5 6 7
1 4 11 26 57
然后做一次差
变成
3 4 5 6 7
1 3 7 15 31
我们可以看到如过吧这串数+1
就得到了
3 4 5 6 7
2 4 8 16 32
发现这些数分别是
2n-2
于是乎
列出式子:2*(2n-2-n)
然后就一手干了:
代码就是:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
const int MOD=19260817;
long long n,x,y,z,ans=0,k;
inline long long kuaisumi(long long x,long long y)
{
long long ret=1;
while(y)
{
if(y&1)
ret=(ret*x)%MOD;
y>>=1;
x=(x*x)%MOD;
}
return ret%MOD;
}
int main()
{
freopen("pcr.in","r",stdin);
freopen("pcr.out","w",stdout);
int i;
scanf("%lld",&n);
if(n==1){printf("0");return 0;}
if(n==2){printf("0");return 0;}
printf("%lld",2*(((2*kuaisumi(2,n-2)-n)%MOD+MOD)%MOD)%MOD);
return 0;
}
2.排列
(arrange.pas/c/cpp)
【背景】
排列组合是高考数学中常见的套路题,而且往往属于大家都会做但是却又做不对的毒瘤题。
图为19年一卷理数15题。
【问题描述】
现在你又被毒瘤高考出题人恶心到了,打算写个cpp教他什么叫排列组合。
给你一个数字串s和一个整数d,统计s有多少种不同的排列(允许前导0)能被d整除。例如114514有20种排列能被2整除。
【输入】
第一行是一个整数T,表示测试数据的个数,接下来T行每行一组用空格隔开的s和d。s保证只含数字0到9
【输出】
共T行,含义如题
【输入输出样例1】
a r r a n g e . i n arrange.in arrange.in
7
000 1
001 1
1234567890 1
123434 2
1234 7
12345 17
12345678 29
a r r a n g e . o u t arrange.out arrange.out
1
3
3628800
90
3
6
1398
【数据范围】
对于20%的数据:s的长度不超过5
对于50%的数据:s的长度不超过8
对于100%的数据:s的长度不超过10,1<=d<=1000,1<=T<=15
================================================
这道题,我本来是可以A了的,却不了,Hash打了吧
h
a
s
h
[
x
]
=
y
hash[x]=y
hash[x]=y写成了
h
a
s
h
[
x
]
=
=
y
hash[x]==y
hash[x]==y
只有40分,改完后,有因为常数太大,被卡成90分了,后来终于A了
我们可以发现s非常小(小个屁)
于是这让我想起了一个以前讲过的函数
next _permutation(a+1,a+len+1);
这可以比较好的把每个数字拆开来排序
但这样如过你用数字存,就不能把零排前面,不过可以用char数组(我没用)
代码
#include<bits/stdc++.h>
using namespace std;
const int N=100;
const int M=6e6;
int MOD=5726017;
int T,d;
string s;
long long n,sum,ans,len;
long long hash[M];
int id[11]={0,1,2,3,4,5,6,7,8,9,10},a[N];
inline int read()
{
int num=0,f=1;
char c=getchar();
for(;c<'0'||c>'9';c=getchar())if(c == '-')f=-1;
for(;c>='0'&&c<='9';c=getchar())num=(num<<1)+(num<<3)+c-48;
return num;
}
inline bool Hash(int x,long long y)
{
if(hash[x]==-1)
{
hash[x]=y;
return 1;
}
if(hash[x]==y)return 0;
while(1)
{
x+=10007;
x%=MOD;
if(hash[x]==y)return 0;
if(hash[x]==-1)
{
hash[x]=y;
return 1;
}
}
}
int main()
{
freopen("arrange.in","r",stdin);
freopen("arrange.out","w",stdout);
int i,j;T=read();
while(T--)
{
cin>>s;
for(ans=0,d=read(),len=s.length(),i=-1;++i<len;)a[i+1]=(int)s[i]-48;
for(i=-1;++i<MOD;)hash[i]=-1;
for(sum=0,i=len+1;--i;)sum=(long long)sum*10+(long long)a[i];
Hash((sum*357*157/79)%MOD,sum);
ans+=(sum%d==0);
while(next_permutation(id+1,id+len+1))
{
sum=0;
for(i=len+1;--i;)sum=(long long)sum*10+(long long)a[id[i]];
if(Hash((sum*357*157/79)%MOD,sum))ans+=(sum%d==0);
}
printf("%lld\n",ans);
}
return 0;
}
被卡常超时,不赖我……
3.新 数 独
(new.pas/c/cpp)
【背景】
高考题太无聊了,于是你van起了新 数 独。
【问题描述】
【输入】
输入共15行,每行一个字符串。左右字符串组合在一起表示所有不等号从上到下从左到右排列的结果。横向不等号用’<’和’>’表示,纵向用’v’和’^’表示
【输出】
输出共9行,每行9个用空格隔开的数字,表示数独中应该填入的数
【输入输出样例1】
n e w . i n new.in new.in
n e w . o u t new.out new.out
4 9 1 7 3 6 5 2 8
2 3 7 8 1 5 6 4 9
5 6 8 2 4 9 7 3 1
9 1 3 6 5 4 8 7 2
8 5 4 9 7 2 1 6 3
7 2 6 3 8 1 9 5 4
3 4 9 5 6 8 2 1 7
1 8 5 4 2 7 3 9 6
6 7 2 1 9 3 4 8 5
============================================
13分做法
输出样例得13分
别吐槽我,因为题目要求解唯一,所以数据还真不好跑
只有8个点发现了
所以我输出样例拿了13分
首先最大的问题就是读入
在遇到这种看上去很麻烦的题的时候首先要做的就是冷静
仔细观察规律,还是有可能找到方便的处理方法的
例如这题,这是我一开始的写法
等等,这……
还有后文:
后来老师发现不对啊 ,我i和j干嘛非得用来控制字符输入
我i和j表示第i行第j列,然后判断是否需要输入不就行了么
于是输入的问题就比较简单(nan) 的解决了
如何快速剪枝?
数独游戏这种的,最好是填一个数就判断是否合法,否则T飞
其实不必剪枝,我们看看还有什么数能填就行了
lv,rv,sv分别代表每行,每列,每块还没填的数
如何取块?
还是观察规律
最后,如何处理不等号
技巧:方向数组
这个也是比较常见的
只要在输入的时候把0~3的方向的大小关系存起来即可
判断的时候就可以做到自动化处理
代码,代码》?》?》?
终于要请教std了……youbiyoubi~~
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int fx[4]={1,-1,0,0},fy[4]={0,0,1,-1};
int a[10][10][4];
int q[10][10];
bool lv[10][10],rv[10][10],sv[10][10];
int gtid(int x,int y){ return (x-1)*9+y-1;}
void Congratulations(){
for(int i=1;i<=9;++i){
for(int j=1;j<=9;++j)
printf("%d ",q[i][j]);
printf("\n");
}
return ;
}
bool chck(int x,int y){ return x>=1&&x<=9&&y>=1&&y<=9;}
bool jdg(int x,int y,int z){
for(int i=0;i<4;++i)
if(chck(x+fx[i],y+fy[i]) && q[x+fx[i]][y+fy[i]]!=-1){
if(!a[x][y][i] && z==q[x+fx[i]][y+fy[i]]) return false;
if(a[x][y][i] && (z-q[x+fx[i]][y+fy[i]])*a[x][y][i]<=0) return false;
}
return true;
}
bool dfs(int z){
if(z==81){
Congratulations();
return true;
}
int x=z/9+1,y=z%9+1,w=(x-1)/3*3+(y-1)/3+1;
for(int i=1;i<=9;++i)
if(!lv[x][i] && !rv[y][i] && !sv[w][i] && jdg(x,y,i)){
q[x][y]=i,lv[x][i]=rv[y][i]=sv[w][i]=true;
if(dfs(z+1)) return true;
q[x][y]=-1,lv[x][i]=rv[y][i]=sv[w][i]=false;
}
return false;
}
int main(){
freopen("new.in","r",stdin);
freopen("new.out","w",stdout);
memset(a,0,sizeof(a));
memset(lv,0,sizeof(lv));
memset(rv,0,sizeof(rv));
memset(sv,0,sizeof(sv));
char ch;
for(int i=1;i<=9;++i){
for(int j=1;j<=9;++j)
if(j%3){
ch=getchar();
while(ch!='<'&&ch!='>') ch=getchar();
a[i][j][2]=(ch=='<' ? -1 : 1);
a[i][j+1][3]=(ch=='<' ? 1 : -1);
}
if(i%3){
for(int j=1;j<=9;++j){
ch=getchar();
while(ch!='v'&&ch!='^') ch=getchar();
a[i][j][0]=(ch=='v' ? 1 : -1);
a[i+1][j][1]=(ch=='v' ? -1 : 1);
}
}
}
for(int i=1;i<=9;++i)
for(int j=1;j<=9;++j)
q[i][j]=-1;
bool hahaha=dfs(0);
if(!hahaha) printf("wocao?\n");
return 0;
}
其实也可以不请教,不是还有几个大佬贼强,A了吗》?
周神zjc的列表:
#include <bits/stdc++.h>
using namespace std;
const int N=233;
int p[N],q[N];
int b[1<<10],c[10],d[10],e[10];
void pre()
{
p[1]=0;q[1]=1;
p[2]=1;q[2]=2;
p[3]=3;q[3]=4;
p[4]=4;q[4]=5;
p[5]=6;q[5]=7;
p[6]=7;q[6]=8;
for (int i=6;++i<16;)
p[i]=i-7,q[i]=i+2;
for (int i=15;++i<22;)
p[i]=p[i-15]+9,
q[i]=q[i-15]+9;
for (int i=21;++i<31;)
p[i]=i-13,q[i]=i-4;
for (int i=30;++i<37;)
p[i]=p[i-15]+9,
q[i]=q[i-15]+9;
for (int i=36;++i<109;)
p[i]=p[i-36]+27,
q[i]=q[i-36]+27;
for (int i=9;~--i;)
c[i]=d[i]=e[i]=(1<<9)-1,
b[1<<i]=i+1;
}
#define C getchar()
char read()
{
char k=C;
while (k^'<'&&k^'>'&&k^'^'&&k^'v')
k=C;
return k;
}
int n;
int a[N][N];
void in()
{
n=108;
for (int i=1;i<=n;++i)
{
char c=read();
if (c=='>'||c=='v')
a[p[i]][q[i]]=1,
a[q[i]][p[i]]=2;
else
a[q[i]][p[i]]=1,
a[p[i]][q[i]]=2;
}
}
int A[N][N];
void QAQ()
{
for (int i=0;i<9;++i,puts(""))
for (int j=0;j<9;++j)
cout<<a[i][j]<<' ';
exit(0);
}
int get(int x,int y)
{
return x*9+y;
}
void dfs(int x,int y)
{
if (x==9) QAQ();
int z=c[x]&d[y]&e[x/3*3+y/3];
int l=1,r=9;
if (x)
{
if (a[get(x,y)][get(x-1,y)]==1) ++l;
if (a[get(x,y)][get(x-1,y)]==2) --r;
}
if (y)
{
if (a[get(x,y)][get(x,y-1)]==1) ++l;
if (a[get(x,y)][get(x,y-1)]==2) --r;
}
if (a[get(x,y)][get(x+1,y)]==1) ++l;
if (a[get(x,y)][get(x+1,y)]==2) --r;
if (a[get(x,y)][get(x,y+1)]==1) ++l;
if (a[get(x,y)][get(x,y+1)]==2) --r;
for (;z;z-=z&-z)
{
int k=b[z&-z];
if (k<l||k>r) continue;
A[x][y]=k;
int u=1<<k-1;
c[x]^=u;
d[y]^=u;
e[y]^=u;
dfs(x+(y+1)/9,(y+1)%9);
c[x]^=u;
d[y]^=u;
e[y]^=u;
}
}
void wor()
{
dfs(0,0);
}
int main()
{
freopen("new.in","r",stdin);
freopen("new.out","w",stdout);
pre();
in();
wor();
}
ybc的急速跑法》
???(无法识别
#include<bits/stdc++.h>
#define ll long long
#define C getchar()
#define Violet inline
#define mem(a,i) memset(a,i,sizeof(a))
using namespace std;
Violet char read()
{
char c=C;
for (;c!='>'&&c!='<'&&c!='v'&&c!='^';c=C);
return c;
}
int f[10][10][5];// 上1 下2 左3 右4
char c[10];
int a[10][10];
int p[10][10];
bool h[10][10],l[10][10],g[10][10];
Violet void opp()
{
for (int i=1;i<=9;i++)
{
for (int j=1;j<=9;j++)
printf("%d ",a[i][j]);
printf("\n");
}
exit(0);
}
Violet void dfs(int x,int y)
{
if (x>9) opp();
int mi=1,ma=9;
if (f[x][y][1]==1) mi=max(mi,a[x-1][y]);
if (f[x][y][1]==-1) ma=min(ma,a[x-1][y]);
if (f[x][y][3]==-1) ma=min(ma,a[x][y-1]);
if (f[x][y][3]==1) mi=max(mi,a[x][y-1]);
for (int i=mi;i<=ma;i++)
if (!h[x][i]&&!l[y][i]&&!g[p[x][y]][i])
{
h[x][i]=1;
l[y][i]=1;
g[p[x][y]][i]=1;
a[x][y]=i;
dfs(x+(y==9),y%9+1);
h[x][i]=0;
l[y][i]=0;
g[p[x][y]][i]=0;
a[x][y]=0;
}
}
int main()
{
freopen("new.in","r",stdin);freopen("new.out","w",stdout);
for (int i=0;i<=6;i+=3)
for (int j=i+1;j<=i+3;j++)
{
for (int k=1;k<=6;k++)
c[k]=read();
for (int k=0;k<=6;k+=3)
{
f[j][k+1][4]=(c[k/3*2+1]=='>')-(c[k/3*2+1]=='<');
f[j][k+2][3]=-f[j][k+1][4];
f[j][k+2][4]=(c[k/3*2+2]=='>')-(c[k/3*2+2]=='<');
f[j][k+3][3]=-f[j][k+2][4];
}
if (j<i+3)
{
for (int k=1;k<=9;k++)
c[k]=read();
for (int k=1;k<=9;k++)
{
f[j][k][2]=(c[k]=='v')-(c[k]=='^');
f[j+1][k][1]=-f[j][k][2];
}
}
}
for (int i=1;i<=9;i++)
for (int j=1;j<=9;j++)
p[i][j]=(i-1)/3*3+(j+2)/3;
dfs(1,1);
return 0;
}
还有很多不一样很好的思路
这里就不展示了吧~
想想不用展示我输出样例的代码了吧……(逃)