2016.8.29 LGTB解题报告
考试总结:
1.这道题还是花了不少的时间的,特别是对拍啊之类的,总是放不下,然后有一些实现的问题导致改错也改了很久;
解决方案:下一次在决定写程序的时候,一定要先在草稿纸上把实现算法的流程基本上写一次,精确到循环语句,判断语句的框架,把所有的情况都完整地考虑一次,确定没有问题了再写;
这道题犯了和上一套第二题同样的问题:证明的时间过长;
解决方案:在解题时把流程规范化,在证明了算法正确性以后就放下了,不要总是把它提出来!
2.这道题的数据范围特别小,所以可以很容易地想到状压DP,然后考虑到互质的概念,就基本可以确定用位运算来表示质因数了,但是如果100个数考虑到极值100个数都变成质数的话,状压肯定是longlong存不下的,而NOIP难度很少考到比longlong范围大的状压,所以我最后只写了40分的程序,并且由于是第一次写状压,所以很荣幸地没调试出来,所以新学的知识点还是要做那么一两道题的,一是为了熟悉知识点与算法本质,二也是为了在考场上不至于那么害怕;然后正解分析了a<=30这个特性之后得出最多取到58,所以这说明,选定了一个算法之后就要去规范化地解决问题,比如边界比如初始化,而不是还盯着那个算法不知道干什么,专注于一个方面才能把这个方面做好;
3.看题;
4.总结得出,我是那种只要文件和输入输出名没问题,没注销,仔细读了题就稳前3的人,当然,我这些方面经常出问题,所以一定要改;
解题报告:
一.LGTB与偶数:
题意:相邻的数同为奇数可以删,同为偶数可以删;
分析:如果遇到可以消的,那么消了以后也不会有坏的影响,因为如果本来被消的两个数也可以和另一边的消除,那么这两个数被删掉后旁边的数凑在一起也可以被消,反之,则本来也不能被消,所以遇到可以消除的就消除,那么一个偶数被消除后留下的肯定是一个奇数,所以就是线性的算法了;
注意:在消的过程中可能出现消完的情况,这时之前的奇偶性已经没有用了,要特殊情况考虑;
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2e9;
int tmp,n,f,flg;
int main()
{
freopen("even.in","r",stdin);
freopen("even.out","w",stdout);
scanf("%d",&n);f=1;
scanf("%d",&tmp);if(tmp%2)flg=1;
for(int i=2;i<=n;i++){
scanf("%d",&tmp);
if(!f){
if(tmp%2)flg=1;else flg=0;
f=1;
}
else if(tmp%2){
if(flg){f--;flg=0;}
else {f++;flg=1;}
}
else{
if(flg){f++;flg=0;}
else {f--;flg=1;}
}
}
printf("%d",f);
return 0;
}
二.LGTB与序列:
题意:求由给定序列变成互质序列的最小增减数;
分析:a<=30的特性可以分析得出a最多取到58,58以内素数很少所以可以直接状压质因子,提取出影响因素;对于选择很多的情况,直接枚举每个情况要比搜索还要节约时间(得多),然后会发现还是会超时,这时候,我们的神器——打表就出现啦!对于一个固定不变的东西,打表是很好的选择,对于a>b,变成数时肯定a变的数大一点比较优,所以快排;;
程序:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int f[80005][105],map[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int a[105];
int bit[59]={0,0,1,2,1,4,3,8,1,2,5,16,3,32,9,6,1,64,3,128,5,10,17,256,3,4,33,2,9,512,7,1024,1,18,65,12,3,2048,129,34,5,4096,11,8192,17,6,257,16384,3,8,5,66,33,32768,3,20,9,130,513};
int n;
const int maxn=1<<16;
bool cmp(int x,int y)
{
if(x>y)return true;
else return false;
}
int main()
{
freopen("seq.in","r",stdin);
freopen("seq.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);sort(a+1,a+n+1,cmp);
memset(f,127,sizeof(f));
for(int i=0;i<maxn;i++)f[i][0]=0;
int mini=min(16,n);
for(int i=1;i<=mini;i++)
for(int j=0;j<maxn;j++)if(f[j][i-1]<2e9)
for(int k=1;k<=58;k++)
{
if(bit[k]&j)continue;
if(abs(a[i]-k)+f[j][i-1]<f[bit[k]|j][i])f[bit[k]|j][i]=abs(a[i]-k)+f[j][i-1];
}
int mini2=mini;mini=2e9;
for(int i=1;i<maxn;i++)mini=min(mini,f[i][mini2]);
for(int i=17;i<=n;i++)mini+=abs(a[i]-1);
printf("%d",mini);
return 0;
}
三.LGTB与大数
题意:对一个字符串经行处理,每次把一个数字替换为一个数字串,最火输出这个数字串对1e9+7取mod的结果;
分析:链表实现过了40分,存不下;正解是从后向前推每个数字最后变成了哪个数,这样可以确定排除这个数代表的数又被改了的问题,是一种操作的逆向化,这道题也要小心纪录转换成的数字串的长度可能会超范围,而长度的唯一作用就是用来快速幂,所以可以直接mod(运用费马小定理,注意1e9+7是质数!这是一条很重要的性质!(虽然没有很强的指向性))
程序:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
long long ans,m[10][100005],ll[11];
long long len[100010],n;
string tov[100005],tmp;
char fr[100010],a[100005];
const int mod=1000000007;
/*int qpow(int u)
{
u%=mod-1;int an=10;
if(!u)return 1;u--;
while(u){
if(u&1)an*=10;
if(u!=1)an*=an;
an%=mod;
u>>=1;
}
return an;
}*/
long long qpow(long long b)
{
b%=mod-1;long long an=10;
long long ss=1;
while(b)
{
if(b&1) ss=ss*an,ss%=mod;
an*=an,an%=mod;
b>>=1;
}
return ss;
}
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
scanf("%s",a);scanf("%I64d",&n);
for(int i=1;i<=n;i++){
getchar();
cin>>tmp;//cout<<tmp;
fr[i]=tmp[0];
len[i]=tmp.size()-3;
tov[i]=tmp.substr(3,len[i]);
}
for(int i=0;i<=9;i++){
m[i][n+1]=i;ll[i]=1;}
for(int i=n;i>=1;i--)for(int k=0;k<=9;k++)if(k==fr[i]-'0'){
long long tot=0;
for(int j=0;j<len[i];j++)if(ll[tov[i][j]-'0']){
int ns=tov[i][j]-'0';
m[k][i]*=qpow(ll[tov[i][j]-'0']);m[k][i]%=mod;
m[k][i]+=m[tov[i][j]-'0'][i+1];m[k][i]%=mod;
tot+=ll[tov[i][j]-'0'];tot%=mod-1;
}
ll[k]=tot;if(!ll[k])ll[k]=mod-1;
}
else{
m[k][i]=m[k][i+1];
}
long long l=strlen(a);
for(int i=0;i<l;i++){
ans*=qpow(ll[a[i]-'0']);ans%=mod;
ans+=m[a[i]-'0'][1]; ans%=mod;
}
printf("%I64d",ans%mod);
return 0;
}