2016.11.2第57套模拟集训三
1.神秘大门
(door.pas/c/cpp)
【题目描述】
最近小K大牛经过调查发现,在WZland的最南方——WZ Antarctica 出现了奇怪
的磁场反应。为了弄清楚这一现象,小K 大牛亲自出马,来到了WZ Antarctica。
小K大牛发现WZ Antarctica 出现了一道神秘的大门。人总有好奇心,小K大牛想打开
这扇神秘大门,看门的后面究竟是什么东西,但用尽什么办法也不能打开这扇门。
突然,门上出现了一些奇怪的字符。凭着敏锐的直觉,小K 认为这些符号就是打
开这扇门的关键, 于是小K 抓紧时间开始研究这些符号。
经过一些时间的研究,小K 大牛发现这些符号其实是一串密码,只有破解了
这个密码, 才能打开那扇神秘大门。这个密码十分简单,他给出了两个很长的字
符串A 和B,你只需要判断B 是否在A 中出现过就可以了,当然如果B 在A
中出现,那么你还需要输出B 的字符在A 中依次出现的位置。
这里解释一下B 在A 中出现的概念,设A=S1S2…SN,B= T1T2…TM,如果存
在一组数K:K1<K2<…<KM,使得B=SK1SK2…SKM,那么就可以认为B 在A 出现
过。比如说A=sdfesad, B=sfsad,那么B 在A 中出现过,因为B 中的字符在A
中依次出现的位置为1 3 5 6 7。
这个解密过程实在太简单了,于是小K 大牛就将这个任务交给了你。由于小K
大牛十分着急,他只给了你1s 的时间。
【输入】
输入数据包含2 行,分别包含一个字符串,第一行输入的是字符串A,第二
行输入的是字符串B。
【输出】
第一行输出一个字符串“Yes”或“No”,如果B 在A 中出现,那么输出“Yes”,
否则输出“No”。
如果你的第一行输出“Yes”,那么在第接下来若干行你需要输出一组数K,使
得B=SK1SK2…SKM,每行一个数;否则第二行为空。如果存在多组数据满足条件,
你需要输出字典序最大的一组。
【输入输出样例1】
door.in door.out
sdfesad Yes
sfsad 1
3
5
6
7
【输入输出样例2】
door.in door.out
abcdef No
acdefg
题解:
第一题啊,简单的我以为我最开始想少了。就是从后往前扫,扫到了就输出return ; o(N)。easy
#include<iostream> #include<cstdio> #include<cstring> #define MN 10000005 using namespace std; char a[MN],b[MN]; int pos[MN],lena,lenb; void readin() { cin>>a; cin>>b; lena=strlen(a); lenb=strlen(b); } void work() { int j=lenb-1; for(int i=lena-1;i>=0;i--) if(a[i]==b[j]) { pos[j]=i+1; j--; if(j==-1) return ; } } void prin() { if(pos[0]==0) {cout<<"No"<<endl; return ;} cout<<"Yes"<<endl; for(int i=0;i<lenb;i++) cout<<pos[i]<<endl; return ; } int main() { freopen("door.in","r",stdin); freopen("door.out","w",stdout); readin(); work(); prin(); return 0; }
2. 集结蚂蚁
(ant.c/cpp/pas)
【题目描述】
雄心勃勃的企业家达伦·克劳斯发现了皮姆博士有关缩小原子间距离的公式并
研发出新一代微型“黄蜂战士”,皮姆博士担忧武器会引发不可挽回的后果,于是
找到斯科特并使他成为了新一代“蚁人”。正逢克劳斯与外商交易黄蜂战衣的那天,
斯科特受命前往摧毁黄蜂战衣并销毁数据,然而一个人的力量是渺小的,斯科特需
要走入一个巨大的蚁穴去召唤蚂蚁与他共同作战。
蚁穴是一个巨大复杂的地带,由n 行m 列组成,每个偶数行存在至少一个蚂蚁
聚集地,同一行的不同蚂蚁聚集地以一堵墙隔开,每个蚂蚁聚集地的大小为Si;
每个奇数行存在连接相邻偶数行中蚂蚁聚集地的路径,一个蚂蚁聚集地可能有多条
路径可以到达。
蚁人可从蚁穴的第一行任何一路口出发,由于时间紧迫,蚁人只能向下一行走,
在这种情况下,蚁人希望你能告诉他如何使经过的蚂蚁聚集地大小之和最大,从而
召唤最多的蚂蚁。
【输入】
第1 行包含两个用空格隔开的整数n,m,意义见描述。
第2 到n+1 行,每行m 个字符(无间隔)且仅存在0 和1:同一偶数行中连
续x 个0 组成一个大小为x 的蚂蚁聚集地,1 为墙体;奇数行中0 表示蚁人可以从
此处通过。
【输出】
对于每组数据输出一个整数,表示经过路径中最大聚集地之和(不包含聚集
地之间的路径)。若蚁人无法到达最后一行则请输出“-1”。
4
【输入样例】
10 10
1111101111
1100001011
1101111111
1100000101
1110110111
1000110001
1101111011
1101000011
1101011111
1101010001
【输出样例】
17
【样例解释】
1111101111
1100001011
1101111111
1100000101
1110110111
1000110001
1101111011
1101000011
1101011111
1101010001
【数据规模】
对于20%的数据,n≤20,m≤20
对于50%的数据,n≤20,m≤100
对于100%的数据,n≤5000,m≤2000,保证n 是一个偶数,路径的数量不多于1×106
一开始想要建图,但是当时以为会超时,毕竟路径都有106那么多。然后大神告诉我们并不会超时,而且数据更大的时候只能建图了。见图可以不管重边,管了的话,反而要超时,索性不理。建完图后,拓扑排序加dp。我个人用的是递推,嗨呀,就是直接dp了,然而当时没有注意中间短路的情况,GG了10分。后来改了就对了。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #define MN 5005 #define MM 2005 using namespace std; int n,m,mapp[MN][MM],f[MM],ans; void inint()//just need dp like in the tree { bool flag; for(int i=n;i>=1;i--) { flag=false; for(int j=1;j<=m;j++) { if(mapp[i][j]==0&&i%2==0)//the qiang is meaningless { int len=0,ma=-100; while(j+len<=m&&mapp[i][j+len]==0) {if(f[j+len]>ma) ma=f[j+len]; len++;} if(ma!=-1) for(int k=j;k<len+j;k++) f[k]=len+ma; j+=len; } if(mapp[i][j]==1) f[j]=-1; } for(int j=1;j<=m;j++) if(f[j]) flag=true; if(flag==false) { ans=-1; return ; } } } int main() { freopen("ant.in","r",stdin); freopen("ant.out","w",stdout); cin>>n>>m; for(int i=1;i<=n;i++) { getchar(); for(int j=1;j<=m;j++) mapp[i][j]=getchar()-'0'; } inint(); for(int i=1;i<=m;i++) if(f[i]>ans) ans=f[i]; if(ans==0) ans=-1; cout<<ans<<endl; return 0; }
3. 计数
(amount. pas/c/cpp)
【题目描述】
我们都爱奎奎,最近奎奎给大家出了一道题。
他告诉了我们一个n*m 的矩阵点,在这个矩阵中的点可以连接成很多线,求
长度等于某一长度的线的条数。
【输入】)
第一行三个正整数n,m,t,第2-t+1 行为所求Wi。
【输出】
输出1 行,共t 个数,第i 个数为满足长度Wi 的条数。
【输入样例1】
3 3 1 例如此为3*3 的矩阵点
1
【输出样例1】
12
【输入样例2】
7 11 1
5
【输出样例2】
168
【输入样例3】
3 5 1
2
【输出样例3】
14
【数据说明】
测试点数据范围
1 2 N<=2000;M<=2000;T<=100
3 4 N<=1000000;M<=1000000;T<=100
5 6 N<=10000000;M<=10000000;T<=100
7 8 N<=100000000;M<=100000000;T<=100
9 10 N<=1000000000;M<=1000000000;T<=100
W<=2max(n,m);
题解:GG,考试的时候没有想出来正解,暴力还错了,因为没有注意到输出居然是一行!因为这道题算的是两个任意点之间的距离等于所求的距离。那么我们可以分为两个部分,一些是在坐标线上的,一些是不在坐标线上但是可以由勾股定理求得。前者很简单,simple这里就不说了。关键是后者枚举勾股数,而且我们晓得这个n和m都很大,直接枚举绝对GG。然后我是没有想出来的,嗨呀,太垃圾了。后来看题解,在那里推了半天,最后写出来还是GG,再一看,d与2*r/d弄混了。嗨呀,气。先把题解复制过来,红色是题解不清楚的地方:
因为x^2+y^2=r^2;
y=sqrt(r^2-x^2);
y=sqrt((r-x)*(r+x));
令d=gcd(r+x,r-x);
设A=(r-x)/d,B=(r+x)/d;
必然存在gcd(A,B)=1;
y^2=d^2*A*B;
A*B是完全平方数;
所以A是完全平方数,B是完全平方数;
设A=a*a,B=b*b;
显然a!=b;
令a<b;
所以a*a=(r-x)/d;
b*b=(r+x)/d;
两式相加得a*a+b*b=2r/d;所以说本来要枚举d从1-2r的,此处只用枚举到根号2r,来优化这个时间
1<d<sqrt(2r);
枚举d;
此时d为2R的约数有两种情况:d=d或d=2R/d。这个地方千万不要弄混了,左边的d一直代表的是上面a*a+b*b=2r/d中的d=2r/d时,只不过是枚举另一个2r的因子,代入的时候还是带分母,不然就GG
第一种情况:d=2r/d。枚举a∈[1,sqrt(2R/2d)] <由2*a*a < 2*r/d转变来>,算出对应的b=sqrt(2r/d-a^2),检查是否此时的A,B满足:A≠B且A,B互质
第二种情况:d=d。枚举a∈[1,sqrt(d/2)] <由2*a*a < d转变来>,算出对应的b=sqrt(d-a^2),检查是否此时的A,B满足:A≠B且A,B互质
可以轻松求出x^2+y^2=r^2得整数解;
然后O(1)查询当前解的个数;
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #define ll long long 6 using namespace std; 7 int n,m,t; 8 ll w; 9 ll gcd_(ll x,ll y) 10 { 11 if(y==0) return x; 12 return gcd_(y,x%y); 13 } 14 void findnum(ll x)//double? 15 { 16 ll tims=0,nowt; 17 ll wid=0,hig=0; 18 if(x<=n) tims+=(n-x)*m; 19 if(x<=m) tims+=(m-x)*n; 20 for(ll d=1;d*d<=2*x;d++) 21 if((2*x)%d==0) 22 { 23 for(ll a=1;a*a*2<=d;a++) 24 { 25 ll b=floor(sqrt(d-a*a)); 26 if(b*b+a*a<d) continue ; 27 if(a==b) continue ; 28 if(gcd_(a,b)!=1) continue ; 29 wid=(b*b-a*a)*(2*x/d)/2,hig=(2*x/d)*a*b; 30 if(wid>=hig) continue ; 31 if(n>=wid&&m>=hig) 32 { 33 nowt=(n-wid)*(m-hig)*2;//from 1/4 part so it's twice 34 tims+=nowt; 35 } 36 if(n>=hig&&m>=wid) 37 { 38 swap(hig,wid); 39 nowt=(n-wid)*(m-hig)*2; 40 tims+=nowt; 41 } 42 } 43 for(ll a=1;a*a*d<=x;a++) 44 { 45 ll b=floor(sqrt(2*x/d-a*a)); 46 if(b*b+a*a<(2*x/d)) continue ; 47 if(a==b) continue ; 48 if(gcd_(a,b)!=1) continue ; 49 wid=(b*b-a*a)*d/2,hig=d*a*b; 50 if(wid>=hig) continue ; 51 if(n>=wid&&m>=hig) 52 { 53 nowt=(n-wid)*(m-hig)*2;//from 1/4 part so it's twice 54 tims+=nowt; 55 } 56 if(n>=hig&&m>=wid) 57 { 58 swap(hig,wid); 59 nowt=(n-wid)*(m-hig)*2; 60 tims+=nowt; 61 } 62 } 63 } 64 cout<<tims<<' '; 65 } 66 int main() 67 { 68 freopen("amount.in","r",stdin); 69 freopen("amount.out","w",stdout); 70 cin>>n>>m>>t; 71 while(t) 72 { 73 cin>>w; 74 findnum(w); 75 t--; 76 } 77 return 0; 78 }