@ivorysi
2019-07-06T12:10:37.000000Z
字数 23839
阅读 882
笔记
这种情况在比较中容易出现
例如
if(a*b<=c) ...
解决方法是把乘法换成除法
if(a<=c/b)
或者开long long
这个略显玄学
就比如说,定义了一个函数
int f(int a,int b,int c,int d);
int main() {
int x=(a,b,c,d);
}
多出现在参数比较多,而且是赋值操作之间
这个……还是注意点吧……
//不能直接找右侧第一个右括号,因为可能有这样((()))()()
//删掉前两个括号时,我们需要找第5个括号更改方向
一开始直接找的右侧第一个括号
if(a=b) {
}
返回值一定是true
解决方法可以开-Wall来提醒
if(1&a > 0)
凡是有二进制运算符的地方一定要打括号
这可能就是脑子短路了
这可能就是犯二了
有些情况下是隐性的
例如长度为100000的string开了100000个
然后就gg
这就真的见祖宗了……造成的后果就是越界,但是和越界还有区别
十年OI一场空,不开long long见祖宗
还可以解决int int 相乘的问题
写着写着就忘了
开-Wall可以解决这类疑难杂症
开-Wall可以解决这类疑难杂症
int sap(int u,int aug) {
if(u==c2*2) return aug;
int flow=0,dmin=2*n-1;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(edge[i].val>0) {
if(dis[u]==dis[v]+1) {
int t=sap(v,min(edge[i].val,aug-flow));
//应该流走的是aug-flow,为什么每次都是网络流写的出问题qwq
edge[i].val-=t;
edge[i^1].val+=t;
flow+=t;
if(flow==aug) break;
if(dis[c1*2-1]>=2*n) return flow;
}
dmin=min(dmin,dis[v]);
}
}
if(!flow){
--gap[dis[u]];
if(gap[dis[u]]==0) dis[c1*2-1]=2*n;
dis[u]=dmin+1;
++gap[dis[u]];
}
return flow;
}
万一原点的水流不够用呢?
经常写了xiaosiji后来按照siji的思路来写,然后gg
就是<=变成了<
这个还是要多加注意
这岂不尴尬?
然后当然就爆零啦……
一定要多生成几组数据卡自己
万一忘记初始化呢?
这个就要手算了,如果空间约束比较大还是深搜吧
例如方格图的大小比较大
scanf("%d%d",&u,&val[u]);
它会先读入val[u],但是u并不是我们想要的下标
siji(k,1,cnt) {
xiaosiji(i,1,k) {//因为不能用k点所以是<k
xiaosiji(j,i+1,k) {
ans=min(ans,g[i][j]+f[i][k]+f[k][j]);
}
}
siji(i,1,cnt) {
siji(j,1,cnt) {
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
必须是小于号
就是在线性处理某个区间,区间的左端点是l,容易习惯性的当1处理
主要表现在我没删注释
bignum operator *(const bignum &b)const {
int lena=v.size(),lenb=b.v.size();
bignum c;c.v.clear();
siji(i,0,lena+lenb) c.v.push_back(0);
xiaosiji(i,0,lena) {
int g=0;
xiaosiji(j,0,lenb) {
c.v[i+j]+=(g+v[i]*b.v[j]);//我写成b.v[i]然后越界了……gg……
g=c.v[i+j]/10;
c.v[i+j]%=10;
}
int t=i+lenb;
while(g) {
c.v[t]+=g;
g=c.v[t]/10;
c.v[t]%=10;
++t;
}
}
gongzi(i,lena+lenb,1) {
if(c.v[i]==0) c.v.pop_back();
else break;
}
return c;
}
bignum operator = (long long x){
v.clear();
do{
v.push_back(x%10);
x/=10;
}while(x);//我写成x>=0,然后死循环了
return *this;
}
for(p=0,j=1;p<n;j*=2,m=p) {//p是排名有多少个
for(p=0,i=n-j;i<n;++i) y[p++]=i;
for(i=0;i<n;++i) if(sa[i]>=j) y[p++]=sa[i]-j;//把>=写成>了
for(i=0;i<n;++i) sortb[i]=x[y[i]];
for(i=0;i<m;++i) sorta[i]=0;//把m写成n了
for(i=0;i<n;++i) ++sorta[sortb[i]];
for(i=1;i<m;++i) sorta[i]+=sorta[i-1];
for(i=n-1;i>=0;--i) sa[--sorta[sortb[i]]]=y[i];
swap(x,y);
for(p=1,x[sa[0]]=0,i=1;i<n;++i)
x[sa[i]]=cmp(y,sa[i],sa[i-1],j) ? p-1:p++;
}
板子题然后gg……
第一个错误导致0不能到y数组里,然而数字从0开始存
把m写成n当n比26小,里面又有较大字符的时候就会gg……
siji(i,1,n) {
if(!askpre(a[i])) pushpre(a[i]);
if(cnt1==cnt2)
{ans+=s-t+1;continue;}//要加上这一句不然总是从s开始去找t
while(cnt1!=cnt2) {
if(p<1) return;
if(askpre(a[p])) {
if(!asksuf(a[p])) pushsuf(a[p]);
--p;
}
else {
while(askpre(a[i+1])) ++i;
pushpre(a[i+1]);++i;
}
}
s=p+1;
t=s;
while(asksuf(a[t])) --t;
++t;
ans+=s-t+1;
}
然后超时了
while(p+1<=n && kuang[p+1].id<=k) {
while(t>h || q[t-1]<=kuang[p+1].val) --t;
//上面这句||应该是&&,如果是||那么它会一直--t然后越界到不知道哪里去了
q[t++]=kuang[p+1].val;
++p;
}
p=1;
while(p<=n && num[p]-1<0) ++p;
siji(i,1,2000000) {
tot-=cnt[0]/2;
cnt[0]-=cnt[0]/2;
if((num[p]-i)==0) {
cnt[0]+=cnt[num[p]];
cnt[num[p]]=0;
}
while(p<=n && num[p]-i<=0) ++p;
if(tot==1 && cnt[0]==1) {printf("%d\n",i);break;}
}
这段代码的bug体现在哪里呢,就是如果我们第一个位置是0,然鹅我们有1这样的数,我们下标显然还指着0,就会gg
我们需要一开始就把下标移到第一个非0位就好啦!!!
我的天我写了这么多年trie树是怎么无伤过每一道题的
难道大家都觉得单纯查字典太傻了……结果我去写单纯查字典就挂了?
不能处理0位!!!
我们需要需要判断左子树有还是没有,右子树有还是没有
int len=k-l2;
if(len!=0)
lson[res-'a'+1]=dfs(l1+1,l1+len,l2,k-1);
len=r2-k;
if(len!=0)
rson[res-'a'+1]=dfs(r1-len+1,r1,k+1,r2);
没有特判就gg
如题啊……
然后多了10倍的空间
宏定义的数要打对啊
void out(int k) {
if(k>=10) {
out(k/10);
}
putchar('0'+k%10);
}
这个函数是不能输出负数的!!!!!
所以要这么写
void out(int k) {
if(k<0) {putchar('-');k=-k;}
if(k>=10) {
out(k/10);
}
putchar('0'+k%10);
}
其实浮点数整数部分超过了int就会发生很大的误差……
定义两个字符串 A,B 相似当且仅当满足以下两个条件中的至少一
个:
(1)A 和 B 相同;
(2)将 A 分为长度相同的两个子串 A0,A1,将 B 分为长度相同的两
个子串 B0,B1,满足 A0 相似于 B0,A1 相似于 B1 或 A0 相似于 B1,
A1 相似于 B0。
给定两个字符串 S,T,问它们是否相似。
有多组数据。
第一行一个整数 t 表示数据组数。
每组数据第一行一个字符串 S,第二行一个字符串 T,保证它们
长度相同。
每组数据一行,若相似输出 YES,不相似输出 NO。
2
abab
baab
aabb
abab
YES
NO
对于 30%的数据,|S|<=30。
对于 60%的数据,|S|<=100。
对于 100%的数据,t<=30,∑|S|<=500000。
这道题本来应该AC的……
然而一开始认为长度为奇数的串是不可能往下拆的,直接输出了NO
实际上两个长度为奇数的串可以一开始就相等……哇QAQ
哇QAQ……
关键那么多年压位压的全都是相同进制的,哇QAQ
火星探险
源程序名 MAR.??? (PAS,C,CPP)
输入文件名 MAR.IN
输出文件名 MAR.OUT
时间限制 1S
空间限制 256MB
问题描述:
在2051年,若干火星探险队探索了这颗红色行星的不同的区域并且制作了这些区域的地图。现在, Baltic空间机构有一个雄心勃勃的计划:他们想制作一张整个行星的地图。为了考虑必要的工作,他们需要知道地图上已经存在的全部区域的大小。你的任务是写一个计算这个区域大小的程序。
任务:
l 从输入文件 mar.in 读取地图形状的描述
l 计算地图覆盖的全部的区域
l 输出到输出文件mar.out
输入:
输入文件mar.in的第一行包含一个整数N(1<=N<=10000),表示可得到的地图数目。
以下N行,每行描述一张地图。
每行包含4个整数x1,y1,x2和y2(0<=x1
输出:
输出文件 mar.out,应该包含一个整数,表示探索区域的总面积(即所有矩形的公共面积)。
样例:
MAR.IN
2
10 10 20 20
15 15 25 30
MAR.OUT
225
这是一道扫描线的题,看上去很像区间加是吧……然后第一反应就是打标记
但是这道题不打标记!!!
为什么呢
因为我们下放了标记之后,一次删除的操作并不会立刻下放,导致我们统计左右子树的时候左子树拥有的线段长度和右子树拥有的线段长度不是真正的线段长度……
然后!这道题利用的是矩形关键的特性!
因为每对一个区间累加我们都会把同样的区间再减去就可以了!!!
我们实际上只需要维护一个线段出现过几次就可以了,直接累加到线段树上就可以了
删除的时候直接减去1
维护cover是覆盖了几次
如果cover>0那么一整条区间都要
如果cover<=0就是左右区间有的线段长度的加和
这道题是不打标记的,因为我们打完标记后的删除操作不能立即下放,导致左右子树 区间统计是错的
这道题是不打标记的!!!!!
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define siji(i,x,y) for(int i=(x);i<=(y);++i)
#define gongzi(j,x,y) for(int j=(x);j>=(y);--j)
#define xiaosiji(i,x,y) for(int i=(x);i<(y);++i)
//#define ivorysi
#define pii pair<int,int>
#define fi first
#define mod 998244353
using namespace std;
typedef long long ll;
int readint() {
int res=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') {
res=res*10+c-'0';
c=getchar();
}
return res;
}
void out(ll x) {
if(x<0) putchar('-'),x=-x;
if(x>=10) {
out(x/10);
}
putchar('0'+x%10);
}
int n;
struct node {
int val,st,ed,cmp;
bool operator < (const node &rhs)const {
return cmp<rhs.cmp;
}
}seg[20005];
struct trnode{
int length,l,r,cover;
}tr[100005];
void build(int u,int l,int r) {
tr[u].l=l;tr[u].r=r;
tr[u].length=tr[u].cover=0;
if(l==r) {
return;
}
int m=(l+r)>>1;
build(u<<1,l,m);
build(u<<1|1,m+1,r);
}
void change(int u,int l,int r,int val) {
if(l==tr[u].l && r==tr[u].r) {
tr[u].cover+=val;
if(tr[u].cover>0) tr[u].length=tr[u].r-tr[u].l+1;
else if(tr[u].l==tr[u].r) tr[u].length=0;
else tr[u].length=tr[u<<1].length+tr[u<<1|1].length;
return;
}
int mid=(tr[u].l+tr[u].r)>>1;
if(r<=mid) {
change(u<<1,l,r,val);
}
else if(l>mid) {
change(u<<1|1,l,r,val);
}
else {
change(u<<1,l,mid,val);
change(u<<1|1,mid+1,r,val);
}
if(tr[u].cover>0) tr[u].length=tr[u].r-tr[u].l+1;
else if(tr[u].l==tr[u].r) tr[u].length=0;
else tr[u].length=tr[u<<1].length+tr[u<<1|1].length;
}
ll ans=0;
void solve() {
int X1,Y1,X2,Y2;
n=readint();
siji(i,1,n) {
X1=readint();
Y1=readint();
X2=readint();
Y2=readint();
seg[2*i-1]=(node){1,X1,X2,Y1};
seg[2*i]=(node){-1,X1,X2,Y2};
}
sort(seg+1,seg+2*n+1);
build(1,1,30000);
change(1,seg[1].st+1,seg[1].ed,seg[1].val);
siji(i,2,2*n) {
ll h=seg[i].cmp-seg[i-1].cmp;
ans+=h*tr[1].length;
change(1,seg[i].st+1,seg[i].ed,seg[i].val);
}
out(ans);putchar('\n');
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
solve();
return 0;
}
int readint() {
int res=0;
char c=getchar();
while(c<'0' || c>'9'){
c=getchar();
}
while(c>='0' && c<='9') {
res=res*10+c-'0';
c=getchar();
}
return res;
}
这个函数是不能读入负数的!!!
int readint() {
int res=0;
int neg=1;
char c=getchar();
while(c<'0' || c>'9'){
if(c=='-') neg=-1;
c=getchar();
}
while(c>='0' && c<='9') {
res=res*10+c-'0';
c=getchar();
}
return res*neg;
}
pli qlmax(int u,int l,int r,int x) {
if(tr[u].l==l && tr[u].r==r) {
if(x!=tr[u].posl) {
return make_pair(tr[u].lmax,tr[u].posl);
}
return make_pair(tr[u].otherl,tr[u].posthl);
}
int m=(tr[u].l+tr[u].r)>>1;
if(r<=m) {
return qlmax(u<<1,l,r,x);
}
else if(l>m) {
return qlmax(u<<1|1,m+1,r,x);
//这里是错的,总是在整个区间下查询的时候一个手滑就写成了m+1……或者上面写成l,m
}
else {
return max(qlmax(u<<1,l,m,x),qlmax(u<<1|1,m+1,r,x));
}
}
siji(k,1,n) {
poi[col[k]].push_back(k);
for(int i=head[k];i;i=edge[i].next) {
int v=edge[i].to;
ind[col[v]]++;
}
}
这个是错的,为什么呢……
因为有可能k和v在一个连通块里啊!!!
siji(k,1,n) {
poi[col[k]].push_back(k);
for(int i=head[k];i;i=edge[i].next) {
int v=edge[i].to;
if(col[v]!=col[k])ind[col[v]]++;
}
}
我写了这个东西绝对不下十次
绝对每次都错
我整理一下写这个东西的思路
1.预备工作
(1)一个大约点数8-10倍的线段树,一个数组就行,存的是下标
(2)一个dist数组,是临时的,是线段树需要维护的,一个dis数组,是最后的答案
(3)一个vis数组!!!!判断这个点的最短路有没有确定下来!!!!
2.算法部分
(1)将dist数组全部赋成正无穷
(2)将起点的dist改成0
(3)建线段树
(4)算法进行n次……这里总是错,干脆下次写成k=n;while(k--) 好了,不然容易弄混,导致计数器是i,写成head[i],然而线段树里的点是u
(5)找出一个点u,将dis[u]赋成dist[u],vis[u]=1
(6)更新最短路的时候判断这个点是不是最短路已经确定了,如果更新了最短路,在线段树里更新这个节点
(7)dist[u]改成正无穷,然后在线段树里修改这个点的值
int tr[MAXN*10],pos[MAXN];
ll dist[MAXN],dis[MAXN],temp;
bool vis[MAXN];
void build(int u,int l,int r) {
if(l==r) {
tr[u]=l;
pos[l]=u;
return;
}
int m=(l+r)>>1;
build(u<<1,l,m);
build(u<<1|1,m+1,r);
if(dist[tr[u<<1]] < dist[tr[u<<1|1]]) {
tr[u]=tr[u<<1];
}
else {
tr[u]=tr[u<<1|1];
}
}
void change(int x) {
int t=pos[x]>>1;
while(t>=1) {
if(dist[tr[t<<1]]<dist[tr[t<<1|1]]) {
tr[t]=tr[t<<1];
}
else {
tr[t]=tr[t<<1|1];
}
t>>=1;
}
}
void dijkstra() {
siji(i,1,n) {
dist[i]=1000000000000000000LL;
}
dist[S]=0;
build(1,1,n);
siji(i,1,n) {
int u=tr[1];
vis[u]=1;
dis[u]=dist[u];
if(u==T) break;
for(int j=head[u];j;j=edge[j].next) {
int v=edge[j].to;
ll w=edge[j].val;
if(vis[v]) continue;
if(dist[v]-w>dist[u]) {
dist[v]=dist[u]+w;
change(v);
}
}
dist[u]=1000000000000000000LL;
change(u);
}
}
就是会有一个数组id[x]记录x在dfs序中的位置,是位置,而不是id[x]就是dfs序
tarjan写错了我是不是要完蛋了
void tarjan(int u) {
dfn[u]=low[u]=++inx;
st.push(u);
instack[u]=1;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[u]==1) {
low[u]=min(dfn[v],low[u]);
}
}
if(low[u]==dfn[u]) {
++tot;
int x;
while(1) {
x=st.top();st.pop();
col[x]=tot;
instack[x]=2;
if(x==u) break;
}
}
}
emmmmmm这个错误显然不好找……看第11行
void tarjan(int u) {
dfn[u]=low[u]=++inx;
st.push(u);
instack[u]=1;
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!dfn[v]) {
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(instack[v]==1) {
low[u]=min(dfn[v],low[u]);
}
}
if(low[u]==dfn[u]) {
++tot;
int x;
while(1) {
x=st.top();st.pop();
col[x]=tot;
instack[x]=2;
if(x==u) break;
}
}
}
我这个傻子把v写成u了
算了这两个东西长得太像了,以后换一个变量名吧
t=(r[a]*r[a]-r[b]*r[b]+dis*dis)/2*dis*r[a];
这是一个余弦定理
然而显然
我没有打上括号
t=(r[a]*r[a]-r[b]*r[b]+dis*dis)/(2*dis*r[a]);
我的天我为什么把i+1打成l+1
我对于小数据用冒泡排序暴力排序……然而我gg了
siji(i,l,r) {
siji(j,l+1,r) {
if(p[j].y < p[i].y) swap(p[j],p[i]);
}
}
有一个地方不对……就是第二层循环不是l+1啊啊啊啊是i+1啊啊啊啊
siji(i,l,r) {
siji(j,i+1,r) {
if(p[j].y < p[i].y) swap(p[j],p[i]);
}
}
for(int i=head[u];i;i=edge[i].to) {
int v=edge[i].to;
if(!vis[v] && v!=fa) {
res=max(res,getdep(v,u));
}
}
叫我RE小能手
for(int i=head[u];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v] && v!=fa) {
res=max(res,getdep(v,u));
}
}
void solve(int u) {
int G=calcG(u);
vis[G]=1;
int mxdp=0;
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) {
int maxdep=getdep(v,G);
mxdp=max(mxdp,maxdep);
ver[maxdep].push_back(make_pair(v,edge[i].val));
}
}
siji(i,0,mxdp) L[i]=Z[i]=-inf;
L[0]=0;
siji(i,0,mxdp) {
int siz=ver[i].size();
xiaosiji(j,0,siz) {
calc(ver[i][j].fi,ver[i][j].se);
int b=min(k,Ln);
ql=1;qr=0;
while(b>=0) {
while(ql<=qr && L[que[qr]]<=L[b]) {--qr;}
que[++qr]=b;--b;
}
siji(h,0,Zn) {
if(Z[h]==-inf) continue;
while(ql<=qr && h+que[ql]+crowd[G]>k) ++ql;
if(ql<=qr && L[que[ql]]!=-inf) {
ans=max(ans,L[que[ql]]+Z[h]);
}
if(ql>qr || h>k) break;
}
if(Ln<Zn) Ln=Zn;
siji(h,0,Zn) L[h]=max(L[h],Z[h]);
siji(h,0,Zn) Z[h]=-inf;
}
ver[i].clear();
}
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) solve(v);
}
}
Ln和Zn都应该是设成0
void solve(int u) {
int G=calcG(u);
vis[G]=1;
int mxdp=0;
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) {
int maxdep=getdep(v,G);
mxdp=max(mxdp,maxdep);
ver[maxdep].push_back(make_pair(v,edge[i].val));
}
}
siji(i,0,mxdp) L[i]=Z[i]=-inf;
L[0]=0;
Ln=0;
siji(i,0,mxdp) {
int siz=ver[i].size();
xiaosiji(j,0,siz) {
Zn=0;
calc(ver[i][j].fi,ver[i][j].se);
int b=min(k,Ln);
ql=1;qr=0;
while(b>=0) {
while(ql<=qr && L[que[qr]]<=L[b]) {--qr;}
que[++qr]=b;--b;
}
siji(h,0,Zn) {
if(Z[h]==-inf) continue;
while(ql<=qr && h+que[ql]+crowd[G]>k) ++ql;
if(ql<=qr && L[que[ql]]!=-inf) {
ans=max(ans,L[que[ql]]+Z[h]);
}
if(ql>qr || h>k) break;
}
if(Ln<Zn) Ln=Zn;
siji(h,0,Zn) L[h]=max(L[h],Z[h]);
siji(h,0,Zn) Z[h]=-inf;
}
ver[i].clear();
}
for(int i=head[G];i;i=edge[i].next) {
int v=edge[i].to;
if(!vis[v]) solve(v);
}
}
我设的是个0啊啊啊啊啊啊啊啊啊
最后的答案该输出负数的时候我输出0啊啊啊啊啊啊
if(id<=mid) {
Insert(tr[y].lc,tr[x].lc,l,mid,id,on);
}
else {
Insert(tr[y].lc,tr[x].rc,mid+1,r,id,on);
}
主席树
似乎……有什么地方不对???
为啥我一直没看出来……
if(id<=mid) {
Insert(tr[y].lc,tr[x].lc,l,mid,id,on);
}
else {
Insert(tr[y].rc,tr[x].rc,mid+1,r,id,on);
}
m=unique(num+1,num+n+cnt)-num-1;
没打+1的我真是瞎了
m=unique(num+1,num+n+cnt+1)-num-1;
附上一个lower_bound正确食用方式
m = lower_bound(num + 1,num + n + cnt + 1) - num;
这回没有-1哦!
我要服了我自己了!!!!
f**k!
void FFT(complex *p,int l,int on) {
brc(p,l);
complex u,t;
for(int h=2;h<=l;h<<=1) {
complex wn(cos(on*2*PI/l),sin(on*2*PI/l));
for(int k=0;k<l;k+=h) {
complex w(1.0,0);
for(int j=k;j<k+h/2;++j) {
u=p[j];
t=w*p[j+h/2];
p[j]=u+t;
p[j+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1) {
xiaosiji(i,0,l) p[i].r/=l;
}
}
看起来似乎很正常???
void FFT(complex *p,int l,int on) {
brc(p,l);
complex u,t;
for(int h=2;h<=l;h<<=1) {
complex wn(cos(on*2*PI/h),sin(on*2*PI/h));
for(int k=0;k<l;k+=h) {
complex w(1.0,0);
for(int j=k;j<k+h/2;++j) {
u=p[j];
t=w*p[j+h/2];
p[j]=u+t;
p[j+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1) {
xiaosiji(i,0,l) p[i].r/=l;
}
}
是/h啊……每次旋转因子是按递归层数不同旋转角度不一样的……啊……
void FFT(complex *p,int l,int on) {
brc(p,l);
complex u,t;
for(int h=2;h<=l;h<<=1) {
complex wn(cos(on*2*PI/h),sin(on*2*PI/h));
for(int k=0;k<l;k+=h) {
complex w(1.0,0);
for(int j=0;j<k+h/2;++j) {
u=p[j];
t=w*p[j+h/2];
p[j]=u+t;
p[j+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1) {
xiaosiji(i,0,l) p[i].r/=l;
}
}
第三层的循环从k开始啊啊啊啊
因为我们枚举递归层数,把序列分成h长的小段,k是这个小段的起点啊啊啊啊
void FFT(complex *p,int l,int on) {
brc(p,l);
complex u,t;
for(int h=2;h<=l;h<<=1) {
complex wn(cos(on*2*PI/h),sin(on*2*PI/h));
for(int k=0;k<l;k+=h) {
complex w(1.0,0);
for(int j=k;j<k+h/2;++j) {
u=p[j];
t=w*p[j+h/2];
p[j]=u+t;
p[j+h/2]=u-t;
w=w*wn;
}
}
}
if(on==-1) {
xiaosiji(i,0,l) p[i].r/=l;
}
}
node *merge(node *x,node *y) {
if(y==null) {
++x->cnt;
return x;
}
if(x==null) {
++y->cnt;
return y;
}
node *res=Newnode();
res->lc = merge(x->lc,y->lc);
res->rc = merge(x->rc,y->rc);
res->sum = res->lc->sum + res->rc->sum;
return res;
}
倒数第二行不能那么写啊……
因为合并一个节点的时候……这个节点显然可以是最后一个节点啊
然后它的两个儿子就什么值也没有,这棵树的值就全错了
node *merge(node *x,node *y) {
if(y==null) {
++x->cnt;
return x;
}
if(x==null) {
++y->cnt;
return y;
}
node *res=Newnode();
res->lc = merge(x->lc,y->lc);
res->rc = merge(x->rc,y->rc);
res->sum = x->sum + y->sum;
return res;
}
ll PathQuery(int x,int y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res += Query(rt[cur_time],1,n,pos[top[x]],pos[x],0,0);
}
if(dep[x] > dep[y]) swap(x,y);
res += Query(rt[cur_time],1,n,pos[x],pos[y],0,0);
return res;
}
似乎????
这不就死循环了吗???!!!
ll PathQuery(int x,int y) {
ll res = 0;
while(top[x] != top[y]) {
if(dep[top[x]] < dep[top[y]]) swap(x,y);
res += Query(rt[cur_time],1,n,pos[top[x]],pos[x],0,0);
x = fa[top[x]];//………………
}
if(dep[x] > dep[y]) swap(x,y);
res += Query(rt[cur_time],1,n,pos[x],pos[y],0,0);
return res;
}
我莫不是个煞笔
void process(int st,int ed,int l,int r) {
top = 0;sumval = 0;
for(int i = st + 1 ; i <= ed ; ++i) {
int cnt = 1;
if(top >= 1 && height[sa[i]] < height[sa[stk[top]]]) {
cnt += siz[top];
sumval -= value[top];
--top;
}
if(sa[i - 1] >= l && sa[i - 1] <= r) {
--cnt;
}
if(cnt != 0) {
stk[++top] = i;
value[top] = 1LL * cnt * (height[sa[i]] - k + 1);
siz[top] = cnt;
sumval += value[top];
}
if(sa[i] >= l && sa[i] <= r) ans += sumval;
}
}
我还看了好久
后来猛然发现……单调栈的弹出不应该是while吗???
void process(int st,int ed,int l,int r) {
top = 0;sumval = 0;
for(int i = st + 1 ; i <= ed ; ++i) {
int cnt = 1;
while(top >= 1 && height[sa[i]] < height[sa[stk[top]]]) {
cnt += siz[top];
sumval -= value[top];
--top;
}
if(sa[i - 1] >= l && sa[i - 1] <= r) {
--cnt;
}
if(cnt != 0) {
stk[++top] = i;
value[top] = 1LL * cnt * (height[sa[i]] - k + 1);
siz[top] = cnt;
sumval += value[top];
}
if(sa[i] >= l && sa[i] <= r) ans += sumval;
}
}
void Rotate(node *u) {
node *v = u->fa , *w = v->fa;
node *b = u == v->lc ? u->rc : u->lc;
if(w) (v == w->lc ? w->lc : w->rc) = u;
v->fa = u; u->fa = w;
if(b) b->fa = v;
if(u == v->lc) {v->lc = b; u->rc = v;}
else {v->rc = b;u->lc = v;}
}
这是一个普通的rotate,然而在LCT中,w的两个儿子可能都不是v
void Rotate(node *u) {
node *v = u->fa , *w = v->fa;
node *b = u == v->lc ? u->rc : u->lc;
if(w && !isRoot(v)) (v == w->lc ? w->lc : w->rc) = u;
v->fa = u; u->fa = w;
if(b) b->fa = v;
if(u == v->lc) {v->lc = b; u->rc = v;}
else {v->rc = b;u->lc = v;}
}
if(c != 1) {
for(int i = 1 ; i <= tot ; ++i) {
pow_c[i][1] = c;
pow_c[i][0] = 1;
if(c > dep[i]) {
pow_c[i][0] = 0;
break;
}
for(int j = 2 ; ; ++j) {
pow_c[i][j] = pow_c[i][j - 1] * c;
if(pow_c[i][j] < dep[i]) {
++pow_c[i][0];
}
else break;
}
for(int j = pow_c[i][0] + 1; j <= 10000 ; ++j) {
pow_c[i][j] = pow_c[i][j - 1] * c % dep[i];
}
pow_t[i][0] = 1;
pow_t[i][1] = pow_c[i][10000];
for(int j = 2 ; j <= 10000 ; ++j) {
pow_t[i][j] = pow_t[i][j - 1] * pow_t[i][1] % dep[i];
}
}
}
SHOI2017相逢是问候,决定快速处理快速幂,分块,每设存10000个c相乘和10000个t相乘,然后快速幂的时候可以快速查表
然后……我没发现这个里面我写了
if(c > dep[i]) {
pow_c[i][0] = 0;
break;
}
然后我就g了,优化一个东西的时候看看前面是不是有些东西和它冲突了
为什么受伤的总是lct
为什么,它会T?
bool isRoot(int u) {
if(lct[u].fa) return true;
return lct[lct[u].fa].lc != u && lct[lct[u].fa].rc != u;
}
这个故事告诉我们,数组版的LCT如果TLE是你指针版的RE(微笑脸)
啥?你跟我说这个isRoot没错啊
呵呵
bool isRoot(int u) {
if(!lct[u].fa) return true;
return lct[lct[u].fa].lc != u && lct[lct[u].fa].rc != u;
}
(卒)
int calcG(int x) {
que[ql = qr = 1] = x;
fa[x] = 0;
while(ql <= qr) {
int now = que[ql++];
size[now] = 1,son[now] = 0;
for(int i = head[now]; i; i = edge[i].next) {
int v = edge[i].to;
if(v != fa[now] && !hascalc[v]) {
que[++qr] = v;
fa[v] = now;
}
}
}
int res = qr;
for(int i = qr ; i >= 1 ; --i) {
int u = que[i];
size[fa[u]] += size[u];
if(size[u] > son[fa[u]]) son[fa[u]] = size[u];
if(son[u] < qr - size[u]) son[u] = qr - size[u];
if(son[res] > son[u]) res = u;
}
return res;
}
啥,你说你没觉得哪错了……
res = qr啊……明明是res = que[qr]才是这个树里的点,都跑到别的树里岂不尴尬啦qwq
void brc(complicated *p) {
for(int i = 0, j = 0; i < L - 1; ++i) {
if(i < j) swap(p[i],p[j]);
int k = L / 2;
while(j >= k) {
j -= k;
k >>= 1;
}
j += k;
}
}
我们把最高位当最低位,而当i = L - 1的时候,虽然不用翻转,但是会使得k减到0,从而死循环,就当为了节省一条语句吧= =
void dfs(int u) {
int G = calc_G(u);
vis[u] = 1;
for(int i = 1 ; i <= qr ; ++i) aux[que[i]].push_back(G);
for(int i = head[G] ; i ; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v]) dfs(v);
}
}
啥,你说错的很明显= =
对啊
是很明显vis[u] = 1和vis[G] = 1
void dfs(int u) {
int G = calc_G(u);
vis[G] = 1;
for(int i = 1 ; i <= qr ; ++i) aux[que[i]].push_back(G);
for(int i = head[G] ; i ; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v]) dfs(v);
}
}
void calc(int u) {
que[ql = qr = 1] = u;
fa[u] = 0;
memset(dp[u],0,sizeof(dp[u]));
while(ql <= qr) {
int now = que[ql++];
for(int i = head[now] ; i ; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v] && fa[u] != v) {
fa[v] = u;
memcpy(dp[v],dp[u],sizeof(dp[now]));
insert(v,val[v]);
que[++qr] = v;
}
}
}
}
啥……你说很正常,我传参就不该用u……因为我很容易认为当前节点就是u
void calc(int u) {
que[ql = qr = 1] = u;
fa[u] = 0;
memset(dp[u],0,sizeof(dp[u]));
while(ql <= qr) {
int now = que[ql++];
for(int i = head[now] ; i ; i = edge[i].next) {
int v = edge[i].to;
if(!vis[v] && fa[now] != v) {
fa[v] = now;
memcpy(dp[v],dp[now],sizeof(dp[now]));
insert(v,val[v]);
que[++qr] = v;
}
}
}
}
这个故事告诉我们不能乱编数据结构?可是我不会啊
这应该是部分可持久化,是个线性的修改
void Merge(int u,int v,bool rb) {
if(getfa(u,rb) == getfa(v,rb)) return;
fa[v].push_back(getfa(u,rb));
if(rb) st.push(v);
}
啥,你说你看不懂
我也看不懂
首先应该是而不是其次是的值需要被存下来,因为我们还要往栈里扔这个东西
void Merge(int u,int v,bool rb) {
int g = getfa(v,rb),q = getfa(u,rb);
if(g == q) return;
fa[g].push_back(q);
if(rb) st.push(g);
}
void add(int u,int v,int c) {
edge[++sumE].to = v;
edge[sumE].next = head[u];
edge[sumE].cap = c;
head[u] = sumE;
}
void addtwo(int u,int v,int c) {
if(u == S) Sflow += c;
add(u,v,c);add(v,u,0);
}
for(int i = 1 ; i <= N ; ++i) {
if(deg[i] > 0) add(S,i,deg[i]);
else add(i,T,-deg[i]);
}
好几次了
我都把addtwo写成了add
for(int i = 1 ; i <= N ; ++i) {
if(deg[i] > 0) addtwo(S,i,deg[i]);
else addtwo(i,T,-deg[i]);
}
我TM是傻x
fuck you fuck me fuck my life
void addtwo(int u,int v,int c) {
add(u,v,c);add(v,u,c);
}
大家好好看这是我网络流的加边!
void addtwo(int u,int v,int c) {
add(u,v,c);add(v,u,0);
}
我居然这道题还有40可真是谢谢出题人
two[i] = fpow(2,C(n - 1,2));
C(n,m)是一个组合数,qumo
我愣是没找出来WA的原因
后来找到了我以前写的代码(多项式求逆,发现指数要取模MOD - 1 = =)
two[i] = fpow(2,1LL * i * (i - 1) / 2 % (MOD - 1));
friend void calc_Inverse(const poly &f,poly &g,int e) {
if(e == 1) {
g.a[0] = fpow(f.a[0],MOD - 2);
g.deg = 0;
return;
}
calc_Inverse(f,g,(e + 1) >> 1);
int M = 1;while(M <= e - 1 + g.deg) M <<= 1;
poly t = f;
for(int i = e ; i < M ; ++i) t.a[i] = 0;
for(int i = g.deg + 1 ; i < M ; ++i) g.a[i] = 0;
NTT(g,1,M);NTT(t,1,M);
for(int i = 0 ; i < M ; ++i) {
g.a[i] = 1LL * g.a[i] * (2 + MOD - 1LL * g.a[i] * t.a[i] % MOD) % MOD;
}
NTT(g,-1,M);
for(int i = e ; i < M ; ++i) g.a[i] = 0;
g.deg = e - 1;
}
这个M呢……显然看看我们下面的乘法式,指数最大的是g.deg * 2 + t.deg
我因为把on和L填反和这个指数设置的问题debug了一上午……
md我简直zz
friend void calc_Inverse(const poly &f,poly &g,int e) {
if(e == 1) {
g.a[0] = fpow(f.a[0],MOD - 2);
g.deg = 0;
return;
}
calc_Inverse(f,g,(e + 1) >> 1);
int M = 1;while(M <= g.deg * 2 + e - 1) M <<= 1;
poly t = f;t.deg = e - 1;
for(int i = e ; i < M ; ++i) t.a[i] = 0;
for(int i = g.deg + 1 ; i < M ; ++i) g.a[i] = 0;
NTT(g,1,M);NTT(t,1,M);
for(int i = 0 ; i < M ; ++i) {
g.a[i] = 1LL * g.a[i] * (2 + MOD - 1LL * g.a[i] * t.a[i] % MOD) % MOD;
}
NTT(g,-1,M);
for(int i = e ; i < M ; ++i) g.a[i] = 0;
g.deg = e - 1;
}
int64 dis[MAXN];
……
……
……
void Add_Line(int u,Line g) {
int l = dis[id[tr[u].L]],r = dis[id[tr[u].R]];
tr[u].val = min(tr[u].val,min(g.cal(l),g.cal(r)));
if(!tr[u].cover) {tr[u].f = g;tr[u].cover = 1;return;}
else {
int mid = (tr[u].L + tr[u].R) >> 1;
mid = dis[id[mid]];
if(tr[u].f.cal(l) >= g.cal(l) && tr[u].f.cal(r) >= g.cal(r)) {
tr[u].f = g;return;
}
else if(tr[u].f.cal(l) <= g.cal(l) && tr[u].f.cal(r) <= g.cal(r)) return;
else if(tr[u].f.cal(l) <= g.cal(l)) {
if(cross(tr[u].f,g) <= mid) {Add_Line(u << 1,tr[u].f);tr[u].f = g;}
else Add_Line(u << 1 | 1,g);
}
else {
if(cross(tr[u].f,g) <= mid) {Add_Line(u << 1,g);}
else {Add_Line(u << 1 | 1,tr[u].f);tr[u].f = g;}
}
}
}
呀,l,r,mid显然就是线段树的区间端点嘛,所以是int
woc我怎么一直WA
……
……
……
去**的l是线段树的区间端点,你不是下面都把l赋成dis了吗,md。。
void Add_Line(int u,Line g) {
int64 l = dis[id[tr[u].L]],r = dis[id[tr[u].R]];
tr[u].val = min(tr[u].val,min(g.cal(l),g.cal(r)));
if(!tr[u].cover) {tr[u].f = g;tr[u].cover = 1;return;}
else {
int64 mid = (tr[u].L + tr[u].R) >> 1;
mid = dis[id[mid]];
if(tr[u].f.cal(l) >= g.cal(l) && tr[u].f.cal(r) >= g.cal(r)) {
tr[u].f = g;return;
}
else if(tr[u].f.cal(l) <= g.cal(l) && tr[u].f.cal(r) <= g.cal(r)) return;
else if(tr[u].f.cal(l) <= g.cal(l)) {
if(cross(tr[u].f,g) <= mid) {Add_Line(u << 1,tr[u].f);tr[u].f = g;}
else Add_Line(u << 1 | 1,g);
}
else {
if(cross(tr[u].f,g) <= mid) {Add_Line(u << 1,g);}
else {Add_Line(u << 1 | 1,tr[u].f);tr[u].f = g;}
}
}
}
你说我也是zz……
这个似乎被zzt指出好几次了啊
“我没WA(RE)”
“你开O2了吗”
“(woc)没”
然后发现自己真的WAorRE……惨兮兮
然而我用三目运算符
void build_sam(int l,int c) {
node *nowp = tail++,*p;
nowp->len = l;nowp->cnt = 1;
for(p = last ; p && !p->nxt[c]; p = p->par) {
p->nxt[c] = nowp;
}
if(!p) nowp->par = root;
else {
node *q = p->nxt[c];
if(q->len == p->len + 1) nowp->par = q;
else {
node *copyq = tail++;
*copyq = *q;
copyq->len = p->len + 1;copyq->cnt = 0;
q->par = nowp->par = copyq;
for(; p && p->nxt[c] == q ; p = p->par) {
p->nxt[c] = q;
}
}
}
last = nowp;
}
然后我们干啥呢
把第17行的q改成copyq啊md
void build_sam(int l,int c) {
node *nowp = tail++,*p;
nowp->len = l;nowp->cnt = 1;
for(p = last ; p && !p->nxt[c]; p = p->par) {
p->nxt[c] = nowp;
}
if(!p) nowp->par = root;
else {
node *q = p->nxt[c];
if(q->len == p->len + 1) nowp->par = q;
else {
node *copyq = tail++;
*copyq = *q;
copyq->len = p->len + 1;copyq->cnt = 0;
q->par = nowp->par = copyq;
for(; p && p->nxt[c] == q ; p = p->par) {
p->nxt[c] = copyq;
}
}
}
last = nowp;
}
void build_suffix_tree() {
Ncnt = tail - pool;
for(int i = 0 ; i < Ncnt ; ++i) {
c[pool[i].len]++;
}
for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
for(int i = 0 ; i < Ncnt ; ++i) {
que[c[pool[i].len]--] = &pool[i];
}
for(int i = 1 ; i <= Ncnt ; ++i) {
if(que[i]->par) {
int f = que[i]->par - pool + 1;
fa[i][0] = f;
add(f,i,que[i]->len - que[i]->par->len);
add(i,f,que[i]->len - que[i]->par->len);
}
if(que[i]->cnt) {id[N - que[i]->len + 1] = i;pos[i] = N - que[i]->len + 1;}
}
}
额,父亲用的是在pool里的标号
儿子用的是在que里的标号
我在干什么
void build_suffix_tree() {
Ncnt = tail - pool;
for(int i = 0 ; i < Ncnt ; ++i) {
c[pool[i].len]++;
}
for(int i = 1 ; i <= N ; ++i) c[i] += c[i - 1];
for(int i = 0 ; i < Ncnt ; ++i) {
que[c[pool[i].len]--] = &pool[i];
}
for(int i = 1 ; i <= Ncnt ; ++i) {
int u = que[i] - pool + 1;
if(que[i]->par) {
int f = que[i]->par - pool + 1;
fa[u][0] = f;
add(f,u,que[i]->len - que[i]->par->len);
add(u,f,que[i]->len - que[i]->par->len);
}
if(que[i]->cnt) {id[N - que[i]->len + 1] = u;pos[u] = N - que[i]->len + 1;}
}
}
sort(val + 1,val + N + 1);
tot = unique(val + 1,val + N + 1) - val - 1;
lower_bound(val + 1,val + N + 1);
这个时候会出锅(手动微笑
事实上是
lower_bound(val + 1,val + tot + 1);
void KM() {
for(int i = 1 ; i <= N ; ++i) {
ex[i][1] = 0;
ex[i][0] = 1e16;
for(int j = 1 ; j <= N ; ++j) {
ex[i][0] = min(ex[i][0],dist(T[i],S[j]));
}
}
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= N ; ++j) slack[j] = 1e16;
memset(vis,0,sizeof(vis));
while(!Match(i)) {
db d = 1e16;
for(int j = 1 ; j <= N ; ++j) d = min(d,slack[j]);
for(int j = 1 ; j <= N ; ++j) {
if(vis[j][0]) ex[j][0] += d;
if(vis[j][1]) {ex[j][1] -= d;slack[j] -= d;}
}
memset(vis,0,sizeof(vis));
}
}
for(int j = 1 ; j <= N ; ++j) ans += dist(T[matc[j]],S[j]);
}
这个slack的!只对!没有访问过的右边点才需要更新!以及d也是对那个取模
void KM() {
for(int i = 1 ; i <= N ; ++i) {
ex[i][1] = 0;
ex[i][0] = 1e16;
for(int j = 1 ; j <= N ; ++j) {
ex[i][0] = min(ex[i][0],dist(T[i],S[j]));
}
}
for(int i = 1 ; i <= N ; ++i) {
for(int j = 1 ; j <= N ; ++j) slack[j] = 1e16;
memset(vis,0,sizeof(vis));
while(!Match(i)) {
db d = 1e16;
for(int j = 1 ; j <= N ; ++j) if(!vis[j][1]) d = min(d,slack[j]);//这里
for(int j = 1 ; j <= N ; ++j) {
if(vis[j][0]) ex[j][0] += d;
if(vis[j][1]) ex[j][1] -= d;
else slack[j] -= d; //还有这里
}
memset(vis,0,sizeof(vis));
}
}
for(int j = 1 ; j <= N ; ++j) ans += dist(T[matc[j]],S[j]);
}