CSP2020复赛前小结
今天用windows下的GUIDE打了一些板子,以下是我遇到的问题。
语言
未定义返回值类型的函数
inline isnum(char ch)
{
return ch>='0'&&ch<='9';
}
inline int read()
{
int x=0,fu=1;
char ch=getchar();
while(!isnum(ch)&&ch!='-') ch=getchar();
if(ch=='-') fu=-1,ch=getchar();
while(isnum(ch)) x=x*10+ch-'0',ch=getchar();
return x*fu;
}
上面是一个快读。isnum(char)忘记写返回值了,但是信息查看栏里面
--------开始编译--------
编译成功.
居然没有报错。
交到洛谷上
ISO C++ 不允许声明无类型的‘isnum’ [-fpermissive]
inline isnum(char ch)
这就很麻烦了啊。考试的时候别看见inline就忘记返回值了!
其实c++有一个在<cstdio>
内的函数isdigit(char)
可以用来判断是否为数字。
这是我一般写的快读(没有负数)
inline int read()
{
int x=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
x=ch-'0';ch=getchar();
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x;
}
读入string
大家应该都知道,读入string
字符串一般用cin>>string
。但是如果想要快一点的话,如何读入呢?用scanf
?
使用scanf
的话,格式符是"%s"
,并且string
变量名不能像char
数组那样当作首元素的指针了,应该使用取地址符来进行读入。
string a;
scanf("%s",&a[0]);
看似很完美也很快的方法,结果读入了什么都没有!这是为什么?
因为string
属于\(STL\)的容器,这些容器以开始都是没有分配内存的,需要手动分配内存。
string a;
a.resize(10);//这个长度要手动设置
scanf("%s",&a[0]);
所以我还是打算老老实实的使用cin呢。
当然有人要问,为什么不用getchar()
再插入。因为string.push_back()
是\(C++11\)的语言了,csp应该是不能用。。。
栈大小
编译开大:
-Wl,-stack=67108864
总体开大:
sudo -s//管理员权限
ulimit -s unlimited//开大
ulimit//检查
算法
快速幂
可能会遇到模数为1的情况。所以别忘记给ans先%一个p或最后再%p一次
一般写法:
for(ans=1;b;b>>=1,a=a*a%p) if(b&1) ans=a*ans%p;
要改成
for(ans=1%p;b;b>>=1,a=a*a%p) if(b&1) ans=a*ans%p;
或
for(ans=1;b;b>>=1,a=a*a%p) if(b&1) ans=a*ans%p;ans%=p;
hack:1 0 1
因为b=0,故使用第一种程序时,不会取模,导致答案错误期望0,读到1
。
质数筛
埃氏筛没有问题,但是在洛谷上跑的时候最多40pts(不加快写20pts),这里就体现了快写线性筛的必要性。
book[1]=1;
for(re LL i=2;i<=n;i++)
{
if(!book[i]) prime[size++]=i,write(i);
for(re LL j=0;j<size&&i*prime[j]<=n;j++)
{
book[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
树状数组
树状数组有个O(1)插入的方法
for(re int i=1;i<=n;i++)
{
a[i]+=read();
if(i+(i&-i)<=n) a[i+(i&-i)]+=a[i];
}
结果我两次把\(1\)看成\(i\),一次忘记判断i+(i&-i)<=n
,挂的可快了!
单调队列
这里只列出来求区间最小值。
-
一定要先判断队列不为空!
-
队尾出不单调元素
-
插入下标进队尾
-
队头出过期元素
记着这些应该就没问题。
while(!mn.empty()&&a[mn.back()]>a[i]) mn.pop_back();
mn.push_back(i);
while(!mn.empty()&&mn.front()+k<=i) mn.pop_front();
if(i>=k) cout<<a[mn.front()]<<" ";
KMP
还是char好用,char下标可以从1开始
for(int i=2,j=0;i<=tl;i++)
{
while(j&&t[i]!=t[j+1]) j=nxt[j];
if(t[i]==t[j+1]) j++;
nxt[i]=j;
}
for(int i=1,j=0;i<=pl;i++)
{
while(j&&(p[i]!=t[j+1])) j=nxt[j];
if(p[i]==t[j+1]) j++;
if(j==tl) cout<<i-tl+1<<endl,j=nxt[j];
}
string只能从0开始,好可怜。。。
int j=-1;
nxt[0]=-1;
for(int i=1;i<b.size();i++)
{
while(j>-1&&b[i]!=b[j+1]) j=nxt[j];
if(b[i]==b[j+1]) j++;
nxt[i]=j;
}
j=-1;
for(int i=0;i<a.size();i++)
{
while(j>-1&&a[i]!=b[j+1]) j=nxt[j];
if(a[i]==b[j+1]) j++;
if(j==b.size()-1){
cout<<i-b.size()+2<<endl;
j=nxt[j];
}
}
逆元
记住三种就好
快速幂
拓展欧几里得
void exgcd(int a,int b,LL &x,LL &y)
{
if(!b)
{
x=1;
y=0;
return;
}
exgcd(b,a%b,y,x);
y-=a/b*x;
}
//------------
exgcd(a,p,x,y);
x=(x%p+p)%p;
cout<<x;
线性
LL inv[100000010];
LL n,p=998244352;
int main()
{
cin>>n;
inv[0]=1;
inv[1]=1;
for(LL i=2;i<=n;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
}
for(int i=1;i<=n;i++) cout<<inv[i]<<" ";
return 0;
}
End
检查long long
,不确定就全开吧!
检查数组大小,无向图双倍,线段树四倍
删除Debug
检查freopen和选手文件夹
赛时
死磕T1,1h30min后估30pts无从下手优化;
看到T2,貌似是组合数学,直接\(pass\)
看到T3,想到暴力——线段树——vector存展开指令——1h30min写完暴力继续T1;
继续T1,优化至能过50pts。再用手动倍增(2000years,10k、100k、1000k、10000kyear)预估时间复杂度为\(O(q*log_{10} n)\),但是大样例3,它\(1s\)只能过一半!
卡常,检查,于是。。。
有所感动的是,赛后代码都正常……呵呵!
无论自己现在做什么,日子还是要接着过,明天的太阳还是会正常升起。
过去的事情就让它过去吧。
After-story
以及待更新