LOG 模拟赛
第一次见尼玛这么给数据范围的……
开考有点困,迷迷糊糊看完了三道题,真的是像老吕说的那样,一道都不会……
思考T1,感觉有点感觉,但是太困了,就先码了暴力,发现打表可以50分,于是就大力打了一波表……转身T3,码出25分的O(n^2)算法,然后不会了……去了T2,码出35分的O(n^2)bfs,然后不会了……+
考试还有1h+,我又看了一遍三道题,发现并没有什么新的思路,于是决定去想T1,继续考试一开始的思路——我发现每加一位,一定是在原合法方案的基础上加的.想到这里,我就迅速码出,发现需要写尼玛高精度,然而考试还剩下不到10min……我估了一下我现在这题的分——70分,于是决定不打高精度了……
最后70+35+25=130……
后来发现我的T1做法基本就是正解了,只要再写上一个漂亮的高精度就可以A掉了……而正解呢,只不过是继续把性质推了一下然后把我的刷表法变成了填表法……
T3正解是,在我的思路的基础之上,利用题目给出的随机树的条件,把我每次都要重新处理的东西记录了下来,这样像动态点分治一样每次查询加修改就可以了……
思维笔记:I.信息共用以减少处理次数 II.利用随机
算法笔记:I.随机树(我们假设其为有根树)树高期望log(实际上都快到根号级别了) II.随机树(我们假设其为有根树)中,每个点的子树树高与其在整棵树中的深度的乘积的加和的期望是n^1.5的
T2好神啊……


雀巢咖啡系列模拟赛 XLI
异化多肽(T1):
看到这个东西还有那个模数就像要去ntt一发,然后开始推式子.
推啊推,推啊推,推出来一个等比数列求和的式子,很开心,感觉自己要A题了.
然后快速码出快速幂ntt+多项式求逆,调完之后,极限数据5s!!!感觉药丸,然后一波卡常,3s左右,就没再管……
最后90,T了一个点,发现一个很蠢蛋的问题——我凑,我快速幂ntt个蛋啊,mdzz,我要快速幂ntt求的那个多项式在mod x^(n+1)意义下是0啊!!!!感觉自己蠢到家了……
所以这题只不过是一个比较裸的多项式求逆,我为什么要快速幂ntt呢?应该就是我多项式这儿太菜了,当然我觉得也有考试的时候脑子不太正常的原因,还有我的极限思想应该是不太好.
推出这道题的多项式求逆还可以直接从生成函数的实际意义和递推式的角度.

#pragma GCC optimize("O3")
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=100010;
const int P=1005060097;
inline int Pow(int x,int y){int ret=1;while(y){if(y&1)ret=(LL)ret*x%P;x=(LL)x*x%P,y>>=1;}return ret;
}
int A[N<<2],ai[N<<2],rev[N<<2],inv[N<<2],len;
int n,m;
inline void ntt(int *C,int opt){register int i,j,k,w;int wn,temp;for(i=1;i<len;++i)if(rev[i]>i)std::swap(C[i],C[rev[i]]);for(k=2;k<=len;k<<=1){wn=Pow(5,(P-1)/k);if(opt==-1)wn=Pow(wn,P-2);for(i=0;i<len;i+=k){w=1;for(j=0;j<(k>>1);++j,w=(LL)w*wn%P){temp=(LL)w*C[i+j+(k>>1)]%P;C[i+j+(k>>1)]=(C[i+j]-temp+P)%P;C[i+j]=(C[i+j]+temp)%P;}}}
}
inline void get_inv(int *a,int *b,int cd){if(cd==1){b[0]=Pow(a[0],P-2);return;}get_inv(a,b,cd>>1),len=cd<<1;int i,ni=Pow(len,P-2);for(i=1;i<len;++i)rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0);memcpy(A,a,cd<<2),memset(A+cd,0,cd<<2);ntt(A,1),ntt(b,1);for(i=0;i<len;++i)b[i]=(2-(LL)b[i]*A[i]%P+P)%P*(LL)b[i]%P;ntt(b,-1);for(i=0;i<cd;++i)b[i]=(LL)b[i]*ni%P;memset(b+cd,0,cd<<2);
}
int main(){scanf("%d%d",&n,&m);int i,x;for(i=1;i<=m;++i)scanf("%d",&x),++ai[x];ai[0]=P-1,len=1;while(len<=n)len<<=1;get_inv(ai,inv,len);printf("%d\n",(P-inv[n])%P);return 0;
}

Kod

编码病毒(T2):
读懂题之后,哇塞,时限有4s,完全可以O(n^2)再来一个1/32的常数用bitset水之啊,然后15min搞好……
最后100,发现正解是比bitset还要水的水fft,就是一个变了一下形的bzoj2194,但是bitset比这玩意好写到不知道哪里去了……

#include <cstdio>
#include <bitset>
#include <cstring>
#include <algorithm>
const int N=100010;
std::bitset<N> test,data,temp;
int n,m,cost[N],cnt[(1<<22)+10];
char s[N];
int main(){scanf("%d%d",&m,&n);int full=(1<<m)-1,i,j,ans=0x3f3f3f3f;memset(cost,0x3f,sizeof(cost)),cost[0]=0;for(i=1;i<=full;++i){cnt[i]=cnt[i-((-i)&i)]+1;cost[i%n]=std::min(cost[i%n],cnt[i]+1);cost[((-i)%n+n)%n]=std::min(cost[((-i)%n+n)%n],cnt[i]);}for(scanf("%s",s),i=0;i<n;++i)data[i]=s[i]=='1';for(scanf("%s",s),i=0;i<n;++i)test[i]=s[i]=='1';for(i=0;i<n;++i){if(i!=0)j=data[0],data>>=1,data[n-1]=j;temp=data^test;j=temp.count(),j=std::min(j,n-j+1);ans=std::min(ans,j+cost[i]);}printf("%d\n",ans);return 0;
}

Bitset

#include <cmath>
#include <cstdio>
#include <cstring>
#include <complex>
#include <algorithm>
typedef double db;
typedef std::complex<db> cd;
const int N=300010;
const db Pi=std::acos(-1.);
cd A[N<<2],B[N<<2];
int test[N<<2],data[N<<2],ci[N<<2],rev[N<<2],len;
int n,m,cost[N],cnt[(1<<22)+10];
char s[N];
inline void fft(cd *C,int opt){register int i,j,k;cd temp;for(i=1;i<len;++i)if(rev[i]>i)std::swap(C[rev[i]],C[i]);for(k=2;k<=len;k<<=1){cd wn(std::cos(2*opt*Pi/k),std::sin(2*opt*Pi/k));for(i=0;i<len;i+=k){cd w(1.,0.);for(j=0;j<(k>>1);++j,w*=wn){temp=w*C[i+j+(k>>1)];C[i+j+(k>>1)]=C[i+j]-temp;C[i+j]+=temp;}}}
}
inline void mul(int *a,int *b,int *c,int n){len=1;while(len<n)len<<=1;int i;for(i=0;i<len;++i){rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0);A[i]=a[i],B[i]=b[i];}fft(A,1),fft(B,1);for(i=0;i<len;++i)A[i]*=B[i];fft(A,-1);db inv=1./len;for(i=0;i<len;++i)c[i]=round(A[i].real()*inv);
}
int main(){scanf("%d%d",&m,&n);int full=(1<<m)-1,i,j,ans=0x3f3f3f3f;memset(cost,0x3f,sizeof(cost)),cost[0]=0;for(i=1;i<=full;++i){cnt[i]=cnt[i-((-i)&i)]+1;cost[i%n]=std::min(cost[i%n],cnt[i]+1);cost[((-i)%n+n)%n]=std::min(cost[((-i)%n+n)%n],cnt[i]);}for(scanf("%s",s),i=0;i<n;++i)data[i]=data[i+n]=s[i]=='1'?1:-1;for(scanf("%s",s),i=0;i<n;++i)test[n-i-1]=s[i]=='1'?1:-1;mul(test,data,ci,n+n+n);for(i=0;i<n;++i){j=(ci[i+n-1]+n)>>1;ans=std::min(ans,cost[i]+std::min(j+1,n-j));}printf("%d\n",ans);return 0;
}

fft

数论函数簇(T3):
考场上从看这道题到最后,我对这道题的解法基本没有变——暴力模拟题意……
最后20分……
看到题解发现这题一道神题……
首先,你在暴力模拟题意的时候,会发现,满足a*a=a(mod n),a*b=0(mod n)的a和b就是满足条件的a和b,然后继续观察,发现满足a*a=a(mod n)的a会对答案贡献gcd(a,n).
然后,我们会发现我们的瓶颈在于找到满足a*a=a(mod n)的a,我们可以把这个式子写成a*(a-1)=0(mod n),发现gcd(a,a-1)=1,这个时候我们可以改变对a要满足的条件的表述——因为a与a-1互质,所以gcd(a,n)与gcd(a-1,n)互质,所以若a满足条件,当且仅当gcd(a,n)与gcd(a-1,n)的乘积为n,也就是说a与a-1中含有不相交且加起来是完整的n的n的两部分.
接着,我们设q为gcd(a-1,n),p为gcd(a,n),并且先假设1<=a<=n,那么a-1从0一直到n-1让q取到了所有的n的因数,我们先把gcd(q,n/q)!=1的q全部舍去,那么剩下了几种q,对于每种q,若想找到一个存在其对应的p的a,就是找到一个含有其对应的p的a模q余1,也就是找到一个k,使得kp%q=1,而k的取值为[1,q],所以对于每一种合法q,都会找到一个互不相同的a,从而做出其对应的p的贡献.也就是说,如果我们放在p的角度上观察,就是对于每一个p,只要gcd(p,n/p)=1,就会做出p的贡献.至此,我们找到了一个新的计算R值的方法:R[n]=∑(d|n)[gcd(d,n/d)=1]*d.那么R[n]就相当于n的所有满足另一半与自己互质的约数和,利用这个我们显然可以用欧拉筛法O(n)得到所有的R,这样我们就得到了50分.
接下来,我们利用我们刚刚提到的式子进行一波反演,大力推一波式子,最后推出来的式子是要1到n^0.5枚举一个值并且每次对n除这个值的平方向下取整得到的数进行除法分块,据说这样的复杂度是O(n^0.5logn),反正可以过……
这道题对于数论推导能力,数论里许多性质的掌握以及化式子的能力考察得很好,是一道很好的题.

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=1000000,P=1005060097,inv2=502530049;
#define mod(a) ((a)<P?(a):(a)%P)
char mu[N+10];
int prime[N+10],len;
bool isnot[N+10];
inline LL calc(LL lim){LL ret=0;for(register LL i=1,last=1;i<=lim;i=last+1){last=lim/(lim/i);ret=(ret+mod(i+last)*mod(last-i+1)%P*inv2%P*mod(lim/i)%P)%P;}return ret;
}
int main(){register int i,j,k;mu[1]=1;for(i=2;i<=N;++i){if(!isnot[i])prime[++len]=i,mu[i]=-1;for(j=1;prime[j]*i<=N;++j){isnot[prime[j]*i]=true;if(i%prime[j]==0){mu[prime[j]*i]=0;break;}mu[prime[j]*i]=-mu[i];}}LL n;int ans=0;scanf("%lld",&n);for(k=1;(LL)k*k<=n;++k)ans=(ans+mu[k]*k*calc(n/((LL)k*k))%P+P)%P;ans=(ans-(n%P)*((n+1)%P)%P*inv2%P+P)%P;printf("%d\n",ans);return 0;
}

真·正确的代码


No.11省选模拟测试
T1:
有一小点点小思想,就是一个简单的树状数组,就是用差分模拟一下模意义下的区间加和.
T2:
满脑子dp……
又是一道网络流思想的贪心,我好像一碰到什么和网络流有关的东西就不会……
只要把费用流建图想清楚,然后用线段树或者堆或者直接sort模拟一下就可以了……
网络流急需加强……我的贪心也好弱啊……
T3:
我写了一个题解上说的60分做法,然而利用矩阵里大部分都是空的,加一下矩阵乘法的优化就过了,还挺快.这种做法就是在原矩阵中加入记录前缀和的部分,在图的模型中就是引入黑洞点.
正解是利用折半的方法求等比数列,这个思想很不错,十分值得积累与借鉴.
这个折半和fft那个折半大异小同.


No.12省选模拟测试
T1:
类似PA round1里的清新简单小贪心.
T2:
我没有梦想……
先根据期望的线性性推出递推式x[i]=x[i+1]+x[i-1]-x[i],然后40分到手……
我到这儿就结束了……
但是实际上加了优化的矩阵乘法是完全可以跑出70分的,而且我考试的时候发现的全局循环节也可以拿到70分,然而我一个都没打,成了一条没有梦想的咸鱼……
正解是利用差分,将问题转化为置换的幂,然后利用轮换O(n)求出.
这道题对于期望线性性以及差分思想的考察很厉害,而且他对于置换群的考察也比较巧妙.
这个差分真的好厉害,我觉得这个递推式与差分的关系需要积累与借鉴,我感觉这个题里x[1],x[n]不变在一定程度上也有一些提示吧.
T3:
原题.


No.14省选模拟测试
这次考试的题都很不错诶……
一开考,看了一遍题.T1,博弈论,凑……T2,容斥计数???T3,数据结构题,好亲切啊,从他开始吧,想了半天,树套树、分块都不行,后来想到了一种set维护的莫队,码了出来,卡了卡常数,感觉50+没问题,就去想T2了.当时不知道为什么突然一阵困意来袭,状态急剧下降,出去上了一趟厕所,回来继续想T2的容斥,感觉麻烦得要死.突然级部召唤我,我去了一趟,没人……回来继续想,感觉好像会用到矩阵树……突然级部又召唤我,我又去了一趟,在那里填了个表,然后迅速回来,发现本来不足时的省选模拟测试被级部占掉了半个多小时……回来快速看了T1,大概想了一个好像并不对的做法,码了出来,竟然过了大样例,交之……然后去码T2的暴力,火速码完两个部分分,加起来30,还剩下不到15min,于是开始想下一个部分分,还是想容斥,还是觉得很麻烦,最后也没想出来……
最后80+30+20=130
T1,莫名奇妙地80,这个东西的思路就是状态转移.有两种方法,第一种是从已知状态向后逆转移,十分清真好理解,第二种是对于每一种状态去搜索其能到达的所有状态从而转移,这种方法十分玄学,而且还容易错,我现在也不是很懂他那个谜一样的环的处理办法……
我的博弈论好弱啊……

#include <cstdio>
#include <cstring>
#include <algorithm>
#define read(a) (scanf("%d",&a))
const int N=7010;
int n,st[2][N],degree[2][N],f[2][N],q[N<<1],front,back;
int main(){read(n);register int i;for(int id=0;id<2;++id){read(st[id][0]);for(i=1;i<=st[id][0];++i)read(st[id][i]),st[id][i]=n-st[id][i];for(i=1;i<=n;++i)degree[id][i]=st[id][0];}f[0][1]=f[1][1]=-1;q[back++]=1,q[back++]=-1;register int x,y,u,v;while(front!=back){x=q[front++];if(x<0)y=-x,x=1,u=0;else y=x,x=0,u=1;if(f[x][y]>0)for(i=1;i<=st[u][0];++i){v=(y+st[u][i]-1)%n+1;if(f[u][v])continue;if(!(--degree[u][v]))f[u][v]=-1,q[back++]=u?-v:v;}elsefor(i=1;i<=st[u][0];++i){v=(y+st[u][i]-1)%n+1;if(f[u][v])continue;f[u][v]=1,q[back++]=u?-v:v;}}for(int id=0;id<2;++id){for(i=2;i<=n;++i){if(f[id][i]==1)printf("Win ");else if(f[id][i]==-1)printf("Lose ");else printf("Loop ");}puts("");}return 0;
}

Kod

T2,我没想出来的那个部分分完全可以用补集转化加矩阵树解决……正解好神啊,矩阵树加生成函数.结合基尔霍夫矩阵的那个结论,我们求出来的主子式行列式就是其所有生成树的边权之积的和.那么如果把原树边的值设为1,把非原树边的值设为x,那么我们的主子式行列式是一个多项式,其前k+1项的和,就是我们的答案.而直接这样求不是很好,那么我们就带入n个值求出对应的答案,利用拉格朗日插值或者高斯消元解方程组来得到多项式系数.
补集转化还真是妙啊……
矩阵树和多项式都是我理解不够深刻的知识点,像利用矩阵树计数,利用生成函数计数,都是我比较弱的地方,拉格朗日插值还是我现学的(现背结论的)……

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=85;
const int P=1000000007;
inline int Pow(int x,int y){int ret=1;while(y){if(y&1)ret=(LL)ret*x%P;x=(LL)x*x%P,y>>=1;}return ret;
}
inline int Inv(int x){return Pow(x,P-2);
}
int n,cao,v[N][N],g[N],co[N],ans[N];
struct Matrix{int a[N][N];inline void clear(){memset(a,0,sizeof(a));}inline void add(int x,int y,int z){a[x][x]+=z,a[y][y]+=z;a[x][y]-=z,a[y][x]-=z;}inline int kir(){int ret=1,b,inv;register int i,j,k;for(i=1;i<n;++i)for(j=1;j<n;++j)a[i][j]=(a[i][j]%P+P)%P;for(i=1;i<n;++i){if(a[i][i]==0){for(j=i+1;j<n;++j)if(a[j][i]){for(k=i;k<n;++k)std::swap(a[i][k],a[j][k]);break;}ret=(P-ret)%P;}inv=Inv(a[i][i]);for(j=i+1;j<n;++j){b=(LL)a[j][i]*inv%P;for(k=i;k<n;++k)a[j][k]=(a[j][k]-(LL)b*a[i][k]%P+P)%P;}ret=(LL)ret*a[i][i]%P;}return ret;}
}Kir;
int main(){scanf("%d%d",&n,&cao);register int i,j,k;for(i=1;i<n;++i){scanf("%d",&j);v[i][j]=v[j][i]=1;}for(i=0;i<n;++i){if(i==0){co[i]=1;continue;}Kir.clear();for(j=0;j<n;++j)for(k=j+1;k<n;++k)Kir.add(j,k,v[j][k]?1:i);co[i]=Kir.kir();}int b,c,l;for(i=0;i<n;++i){memset(g,0,sizeof(g));b=co[i],l=1,g[0]=1;for(j=0;j<n;++j){if(j==i)continue;b=(LL)b*Inv((i-j+P)%P)%P;c=(P-j)%P;for(k=l;k>0;--k)g[k]=((LL)g[k]*c%P+g[k-1])%P;g[0]=(LL)g[0]*c%P;++l;}for(j=0;j<n;++j)ans[j]=(ans[j]+(LL)g[j]*b)%P;}int answer=0;for(i=0;i<=cao;++i)answer=(answer+ans[i])%P;printf("%d\n",answer);return 0;
}

Kod

T3,这题真好,只不过我的那30分呢,明明本机可以50的啊……以后还是把本机测试速度当作参考吧,实际复杂度才是最能说明问题的,毕竟评测环境不同,速度也不同嘛.在我的做法里把我用set搞的东西全部用链表处理出来就可以70了,再稍微卡卡常数(似乎还有分块排序这种卡常操作)就可以A掉了……利用KD_tree,把询问看作点,每个点看作对一个矩形的操作,应该就能50分,但是如果把点按照从大到小的顺序进行操作,再加上一些打标记的技巧,会更加得稳……正解的话,好神啊……我感觉这题在前两个部分分里加入权值随机的条件就有一些暗示……我们从点对的角度观察性质,或者说从一个点的贡献的角度,就会发现对于一个点i,大于它的j,可能和他做出贡献的j的a一定是单调的,而且对于值大于他的和值小于他的j,单调性不同.只是利用这个性质话,可以在随机权值中get到50分,因为据说随机权值下,对于一个点,他后面的点单调起来的个数是logw的.我们继续观察,同时强化我们的式子,就可以得到题解的最终的做法,十分好打.
这才叫数据结构题啊……
感觉用链表预处理前驱后继以代替平衡树是一种十分优秀的做法,很值得积累与借鉴……
还有这个KD_tree的姿势也是很牛逼……
正解就更不用说了,对着数据结构题推式子,利用权值,再加上利用约束的单调性等等……真是厉害啊……

#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
template <typename _t>
inline void read(_t &x){register char ch=gtc;bool ud=false;for(x=0;ch<'0'||ch>'9';ch=gtc)if(ch=='-')ud=true;for(;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+ch-'0',ch=gtc);if(ud)x=-x;
}
typedef long long LL;
typedef std::multiset<int> msi;
typedef std::multiset<int>::iterator mit;
const int L=150;
const int C=100010;
const int N=100010;
const int M=1000010;
const int oo=1000000000;
const int Inf=0x7f7f7f7f;
msi st;
mit re[L<<1];
int len;
int n,m,ans[M],a[N],pos[N],cnt,min;
struct Q{int l,r,id;
}q[M];
inline bool comp(Q aa,Q bb){return pos[aa.l]<pos[bb.l]||(pos[aa.l]==pos[bb.l]&&aa.r<bb.r);
}
inline mit ins(int key){mit ret,tmp,it;ret=tmp=it=st.insert(key);++tmp;min=std::min(min,std::abs(key-*tmp));--it;min=std::min(min,std::abs(key-*it));return ret;
}
inline int force_query(int l,int r){if(r-l+1<=50){register int i,j,ret=Inf;for(i=l;i<=r;++i)for(j=i+1;j<=r;++j)ret=std::min(ret,std::abs(a[i]-a[j]));return ret;}min=Inf,st.clear();st.insert(Inf),st.insert(oo-Inf);for(register int i=l;i<=r;++i)ins(a[i]);return min;
}
inline int round_query(int aim){int p=pos[aim]*L+1,ret=min;len=0;while(p!=aim)re[++len]=ins(a[--p]);std::swap(ret,min);for(int i=1;i<=len;++i)st.erase(re[i]);return ret;
}
inline void quick_query(int l,int r){min=Inf,st.clear();st.insert(Inf),st.insert(oo-Inf);register int i,p=pos[q[l].l]*L;for(i=l;i<=r;++i){while(p!=q[i].r)ins(a[++p]);ans[q[i].id]=round_query(q[i].l);}
}
int main(){read(n);register int i,l,r;for(i=1;i<=n;++i)read(a[i]),pos[i]=(i-1)/L+1;read(m);for(i=1;i<=m;++i){read(l),read(r);if(r-l+1<=L)ans[i]=force_query(l,r);else q[++cnt]=(Q){l,r,i};}std::sort(q+1,q+(cnt+1),comp);for(l=1,r=l;l<=cnt;l=r+1,r=l){while(r+1<=cnt&&pos[q[l].l]==pos[q[r+1].l])++r;quick_query(l,r);}for(i=1;i<=m;++i)printf("%d\n",ans[i]);return 0;
}

考试代码(20分)

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
template <typename _t>
inline void read(_t &x){register char ch=gtc;bool ud=false;for(x=0;ch<'0'||ch>'9';ch=gtc)if(ch=='-')ud=true;for(;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+ch-'0',ch=gtc);if(ud)x=-x;
}
const int L=100,N=100010,M=1000010,oo=1000000000,Inf=0x7f7f7f7f;
#define del(i) (lq[lh[a[i]]]=lq[a[i]],lh[lq[a[i]]]=lh[a[i]])
#define ins(i) (lq[lh[a[i]]]=a[i],lh[lq[a[i]]]=a[i])
#define get(i) (val[i]=std::min(std::abs(key[lq[a[i]]]-key[a[i]]),std::abs(key[lh[a[i]]]-key[a[i]])))
int n,a[N],key[N],temp[N],pos[N];
int lq[N],lh[N];
int val[N],min[N];
inline void Init(){for(register int i=1;i<=n;++i)lq[i]=i-1,lh[i]=i+1;lh[0]=1,lq[n+1]=n;
}
inline bool comp1(int x,int y){return a[x]<a[y];}
struct Q{int l,r,id;}q[M];
int m,ans[M],cnt;
inline bool comp2(Q aa,Q bb){return pos[aa.l]<pos[bb.l]||(pos[aa.l]==pos[bb.l]&&aa.r>bb.r);
}
inline int force_query(int l,int r){register int i,len=0,ret=Inf;for(i=l;i<=r;++i)temp[++len]=key[a[i]];std::sort(temp+1,temp+(len+1));for(i=1;i<len;++i)ret=std::min(ret,temp[i+1]-temp[i]);return ret;
}
inline void quick_query(int l,int r){memset(min,0x7f,sizeof(min));register int i,j,p=pos[q[l].l]*L,wq=n,tmp;Init();for(i=1;i<=p;++i)del(i);for(i=n;i>p;--i)get(i),del(i);for(i=p+1;i<=n;++i)min[i]=std::min(min[i-1],val[i]);Init();for(i=1;i<=p-L;++i)del(i);for(i=l;i<=r;++i){while(wq!=q[i].r)del(wq),--wq;tmp=min[q[i].r];for(j=p-L+1;j<=p;++j)get(j),del(j);for(j=p;j>p-L;--j)ins(j);for(j=p;j>=q[i].l;--j)tmp=std::min(tmp,val[j]);ans[q[i].id]=tmp;}
}
int main(){read(n);register int i,l,r;for(i=1;i<=n;++i)read(a[i]),pos[i]=(i-1)/L+1,temp[i]=i;std::sort(temp+1,temp+(n+1),comp1);for(i=1;i<=n;++i)key[i]=a[temp[i]],a[temp[i]]=i;key[0]=oo-Inf,key[n+1]=Inf;read(m);for(i=1;i<=m;++i){read(l),read(r);if(pos[l]==pos[r])ans[i]=force_query(l,r);else q[++cnt]=(Q){l,r,i};}std::sort(q+1,q+(cnt+1),comp2);for(l=1,r=l;l<=cnt;l=r+1,r=l){while(r+1<=cnt&&pos[q[l].l]==pos[q[r+1].l])++r;quick_query(l,r);}for(i=1;i<=m;++i)printf("%d\n",ans[i]);return 0;
}

莫队(100分)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){R char ch=gtc;for(x=0;ch<'0'||ch>'9';ch=gtc);for(;ch>='0'&&ch<='9';x=(x*10)+ch-'0',ch=gtc);
}
const int N=100010,M=N*10,oo=1e9,Inf=0x7f7f7f7f;
struct Segment_Tree{Segment_Tree *ch[2];int min;Segment_Tree(){ch[0]=ch[1]=NULL,min=Inf;}inline void* operator new(size_t);
}*C,*mempool,*root;
inline void* Segment_Tree::operator new(size_t){if(C==mempool){C=new Segment_Tree[(1<<17)+10];mempool=C+(1<<17)+10;}return C++;
}
#define mid ((l+r)>>1)
inline void insert(Segment_Tree *&p,int l,int r,int pos,int key){if(!p)p=new Segment_Tree;p->min=std::min(p->min,key);if(l==r)return;if(pos<=mid)insert(p->ch[0],l,mid,pos,key);else insert(p->ch[1],mid+1,r,pos,key);
}
inline int query(Segment_Tree *p,int l,int r,int z,int y){if(!p)return Inf;if(z<=l&&r<=y)return p->min;int ret=Inf;if(z<=mid)ret=std::min(ret,query(p->ch[0],l,mid,z,y));if(mid<y)ret=std::min(ret,query(p->ch[1],mid+1,r,z,y));return ret;
}
int n,m,ans[M],tree[N],a[N];
struct Qu{int l,r,id;}q[M];
inline bool comp(Qu aa,Qu bb){return aa.l<bb.l;}
inline void U(R int pos,int key){for(;pos<=n;pos+=pos&(-pos))tree[pos]=std::min(tree[pos],key);
}
inline int Q(R int pos){R int ret=Inf;for(;pos>0;pos-=pos&(-pos))ret=std::min(ret,tree[pos]);return ret;
}
inline void work(){root=NULL;memset(tree,0x7f,sizeof(tree));std::sort(q+1,q+(m+1),comp);R int i,get,pos=m;for(i=n;i>0;--i){get=query(root,0,oo,a[i],oo);while(get<=n){U(get,a[get]-a[i]);get=query(root,0,oo,a[i],((a[i]+a[get])>>1)-1);}insert(root,0,oo,a[i],i);while(pos&&q[pos].l==i)ans[q[pos].id]=std::min(ans[q[pos].id],Q(q[pos].r)),--pos;}
}
inline void rev(){std::reverse(a+1,a+n+1);for(R int i=1;i<=m;++i){q[i].l=n-q[i].l+1,q[i].r=n-q[i].r+1;std::swap(q[i].l,q[i].r);}
}
int main(){read(n);R int i,l,r;for(i=1;i<=n;++i)read(a[i]);read(m);for(i=1;i<=m;++i){read(l),read(r);q[i]=(Qu){l,r,i};ans[i]=Inf;}work(),rev(),work();for(i=1;i<=m;++i)printf("%d\n",ans[i]);return 0;
}

正解(100分)

对于本次考试的经验教训的话:
I.傍若无人,不要受周围人的影响
II.数据结构题,速战速决,不要让他占用太多的时间
III.想尽办法提升考试状态


锅++模拟赛
虽然锅了很多题,比赛完也没有题解,但是这套题还是很6666666666666666666
日常先看一遍题.T1,有点PA的感觉,不太会.T2,什么鬼.T3,卡特兰??
决定先看T3,突然收到消息,两个样例都错了,woc,这题不会还改吧.随后去看T2,想到了30分做法,感觉另外30分有料,但是想去先看一发T1.一看,没什么思路,看到了链的分,记得是lis,证了一下,果然是,怎么推广到树上呢?继续观察,发现题意就是让我把这个树变成小根堆啊,那我就是求最大点数小根堆?证了一下,果然是,兴奋,想到了一种线段树合并的打法,迅速码出,一拍,各种bug,最后发现整个思路都是错的,继续思考,试图挽回,经过了长时间的思考,再加上时间不等人,我就摸索着重新码,一边码,一边想,一边改,最后码了出来,眼动查错一波,开始拍,拍上了!!!一看表,尼玛还剩不到一小时,我剩下俩题还没管呢,于是迅速码出T2的30分做法,过了大样例,然后滚到T3,推出来一个50分的式子,迅速码完过了两个样例,还剩不到2min,把题交上,长吁一口气……
最后100+30+50=180分
T1用的时间太长了,正解1k,我4k.我打了一个线段树启发式合并,我的题解+Kod:

解法:线段树启发式合并
结合10分的链的做法,我们可以像求lis一样求一个最大点数小根堆.
我们先说一下40分的dp,f[i][j]表示以i为根的子树里形成的小根堆堆顶权值大于等于j的小根堆点数最大值,然后dp转移的时候,把儿子的f数组对应位加和,再把选自己产生的贡献算入即可.
我们考虑用数据结构优化这一过程,我们发现对于“以i为根的子树里形成的小根堆堆顶权值”只可能是"以i为根的子树中点的权值",那么我们利用这一点,用线段树存储“以i为根的子树里形成的小根堆堆顶权值”以及每个权值对应的小根堆点数最大值,但是我们发现我们这样做,对于上面那个dp转移的过程是很难在一个可行时间复杂度内实现的.我们考虑改变存储方式,我们利用线段树存储“以i为根的子树里形成的小根堆堆顶权值”以及每个权值对应的"堆顶权值大于等于他的小根堆点数最大值",这个时候,我们在实现上述dp转移过程时,可以利用启发式合并,把点数较小的线段树暴力还原为数组集合,然后再利用这个数组去更新点数较大的线段树,最后把选自己产生的贡献算入.我们dfs整棵树同时启发式合并,最后得到的根的线段树里的所有权值的"堆顶权值大于等于他的小根堆点数最大值"的最大值就是我们答案.
时间复杂度O(nlog^2n).

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){R char ch=gtc;for(x=0;ch<'0'||ch>'9';ch=gtc);for(;ch>='0'&&ch<='9';x=(x*10)+ch-'0',ch=gtc);
}
const int N=200010;
struct V{int to,next;
}c[N<<1];
int head[N],t;
inline void add(int x,int y){c[++t].to=y,c[t].next=head[x],head[x]=t;
}
int keep[N],kk[N],len;
struct Segment_Tree{Segment_Tree *ch[2];int max;Segment_Tree(){ch[0]=ch[1]=NULL,max=0;}inline void pushdown(){if(!max)return;if(ch[0])ch[0]->max+=max;if(ch[1])ch[1]->max+=max;max=0;}inline void* operator new(size_t);
}*root[N],*C,*mempool;
inline void* Segment_Tree::operator new(size_t){if(C==mempool){C=new Segment_Tree[(1<<16)+10];mempool=C+(1<<16)+10;}return C++;
}
#define mid ((l+r)>>1)
inline void insert(Segment_Tree *&p,int l,int r,int pos,int key){if(!p)p=new Segment_Tree;if(l==r){p->max=key;return;}p->pushdown();if(pos<=mid)insert(p->ch[0],l,mid,pos,key);else insert(p->ch[1],mid+1,r,pos,key);
}
inline void add_up(Segment_Tree *p,int l,int r,int z,int y,int key){if(!p)return;if(z<=l&&r<=y){p->max+=key;return;}p->pushdown();if(z<=mid)add_up(p->ch[0],l,mid,z,y,key);if(mid<y)add_up(p->ch[1],mid+1,r,z,y,key);
}
inline void dfs(Segment_Tree *p,int l,int r){if(!p)return;if(l==r){keep[++len]=l,kk[len]=p->max;return;}p->pushdown();dfs(p->ch[0],l,mid);dfs(p->ch[1],mid+1,r);
}
inline int lb(Segment_Tree *p,int l,int r,int pos){if(!p)return 0;if(l==r)return p->max;R int ret;p->pushdown();if(pos<=mid){ret=lb(p->ch[0],l,mid,pos);if(ret==0&&p->ch[1])ret=lb(p->ch[1],mid+1,r,pos);}else ret=lb(p->ch[1],mid+1,r,pos);return ret;
}
inline int pb(Segment_Tree *p,int l,int r,int pos){if(!p)return 0;if(l==r)return p->max;R int ret;p->pushdown();if(pos<=mid)ret=pb(p->ch[0],l,mid,pos);else{ret=pb(p->ch[1],mid+1,r,pos);if(ret==0&&p->ch[0])ret=pb(p->ch[0],l,mid,pos);}return ret;
}
inline int del(Segment_Tree *&p,int l,int r,int pos){if(!p)return 0;if(l==r){p=NULL;return l;}R int ret;if(pos<=mid)ret=del(p->ch[0],l,mid,pos);else{ret=del(p->ch[1],mid+1,r,pos);if(ret==0&&p->ch[0])ret=del(p->ch[0],l,mid,pos);}if(p->ch[0]==NULL&&p->ch[1]==NULL)p=NULL;return ret;
}
int n,m,size[N],a[N],b[N],pos[N];
inline bool comp(int x,int y){return a[x]<a[y];}
inline void dfs(int x,int fa){R int i,j,last,temp;for(i=head[x];i;i=c[i].next){if(c[i].to==fa)continue;dfs(c[i].to,x);if(size[x]<size[c[i].to])std::swap(root[x],root[c[i].to]);len=0,dfs(root[c[i].to],1,m);size[x]+=size[c[i].to];for(j=1,last=1;j<=len;++j){temp=lb(root[x],1,m,keep[j]);if(last!=keep[j])add_up(root[x],1,m,last,keep[j],kk[j]);insert(root[x],1,m,keep[j],kk[j]+temp);last=keep[j]+1;}}i=lb(root[x],1,m,b[x])+1;insert(root[x],1,m,b[x],i);last=b[x]-1;while(last){temp=pb(root[x],1,m,last);if(temp==0||temp>=i)break;last=del(root[x],1,m,last)-1;}++size[x];
}
int main(){read(n);R int i,x,y;for(i=1;i<=n;++i)read(a[i]),pos[i]=i;std::sort(pos+1,pos+(n+1),comp);for(i=1;i<=n;++i){if(i==1||a[pos[i]]!=a[pos[i-1]])++m;b[pos[i]]=m;}for(i=1;i<n;++i){read(x),read(y);add(x,y),add(y,x);}dfs(1,0);int ans=n-lb(root[1],1,m,1);printf("%d\n",ans);return 0;
}

Kod

正解超NB啊.
正解的思路和我的一样,连代码实现的板块都一样,只不过他尼玛差分了最大值啊,差分了最大值啊!!!
回忆一下我的做法,我维护了线段树里信息的单调性,而且合并两颗线段树后,新的线段树仍然满足单调性,那如果我们差分了后缀的话,也是没问题的,不过这个时候我们仍然需要维护单调性,然而实际上,破坏单调性的源头是出现相同权值,也就是在差分的时候出现了0,这个时候在每次算入自己的贡献的时候解决就好了.

#include <map>
#include <vector>
#include <cstdio>
#define pb push_back
#define ft first
#define sd second
#define read(a) (scanf("%d",&a))
const int N=200010;
int n,a[N];
std::vector<int> g[N];
std::map<int,int> f[N];
inline void dfs(int x,int fa){for(auto p:g[x])if(p!=fa){dfs(p,x);if(f[x].size()<f[p].size())f[x].swap(f[p]);for(auto q:f[p])f[x][q.ft]+=q.sd;}++f[x][a[x]];auto it=f[x].find(a[x]);if(it!=f[x].begin()){--it,--it->sd;if(!it->sd)f[x].erase(it);}
}
int main(){read(n);int i,x,y;for(i=1;i<=n;++i)read(a[i]);for(i=1;i<n;++i){read(x),read(y);g[x].pb(y),g[y].pb(x);}dfs(1,0);int ans=n;for(auto p:f[1])ans-=p.sd;printf("%d\n",ans);return 0;
}

Kod

思维笔记:
I.从链推广到树是一用由简单到复杂的思考方式,十分值得借鉴.
II.利用单调性与维护单调性的方式,是一个值得思考的事情,
III.差分似乎与单调性有什么联系.
IV.差分解决问题好像十分有效,之前遇到过的差分异或、差分转置换、树上差分、差分概率等等,都十分巧妙.
算法笔记:
I.map、set的swap可以O(1)交换两个容器里的全部内容.
II.神奇的auto,我并不会用.
*III.排序的启发式合并会变快?
T2考试的时候没仔细想,赛后想想另外30分,并不难,在有了另外30分的思想基础后,正解也并不难……
这种思想算什么?权值排序后按权值增量构造?瞎搞?总之,从易到难的思想是有的.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define R register
typedef long long LL;
char xB[(1<<15)+10],*xS,*xT;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){R char ch=gtc;for(x=0;ch<'0'||ch>'9';ch=gtc);for(;ch>='0'&&ch<='9';x=(x*10)+ch-'0',ch=gtc);
}
const int N=200010;
const int P=1000000007;
const int inv2=500000004;
struct Seg{int l,r,key;
}seg[N];
inline bool comp(Seg a,Seg b){return a.key<b.key;
}
int n,m,size,a[N],pr[N],nt[N],cnt[N],sum,ji[N],ou[N];
#define SUM(a) ((LL)(a)*((a)+1)/2)
inline void insert(int l,int r){R int z=l,y=r,len;if(pr[l-1]!=-1){z=pr[l-1],len=l-pr[l-1];sum=(sum-ji[len]+P)%P;}if(nt[r+1]!=-1){y=nt[r+1],len=nt[r+1]-r;sum=(sum-ji[len]+P)%P;}len=y-z+1;pr[y]=z,nt[z]=y;sum=(sum+ji[len])%P;
}
inline int modify(){R LL ret=sum;if(nt[1]!=-1&&pr[n]!=-1){R int l,r;l=nt[1],r=n-pr[n]+1;ret=ret-ji[l]-ji[r]+SUM(l);if((--l)>r)std::swap(l,r);r=l+r,l=r-l-l;ret=ret+ou[r]-ou[l]+ji[l];ret%=P;}return ret;
}
int main(){read(n);R int i,j,k;ji[0]=0,ou[0]=0,ou[1]=1;for(i=1;i<=n;++i)read(a[i]),ji[i]=(ji[i-1]+SUM(i))%P;for(i=2;i<=n;++i)ou[i]=(ou[i-2]+SUM(i))%P;memset(pr,-1,sizeof(pr));memset(nt,-1,sizeof(nt));for(i=1,j=1;i<=n;i+=j,j=1){while(i+j<=n&&a[i+j]==a[i])++j;seg[++m]=(Seg){i,i+j-1,a[i]};}std::sort(a+1,a+(n+1));std::unique(a+1,a+(n+1));std::sort(seg+1,seg+(m+1),comp);for(i=1,j=1;i<=m;i+=j,j=1){while(i+j<=m&&seg[i+j].key==seg[i].key)++j;if(i+j>m)break;++size;for(k=i;k<i+j;++k)insert(seg[k].l,seg[k].r);cnt[size]=modify();}++size;cnt[size]=(LL)n*(n+1)/2%P;cnt[size]=(LL)cnt[size]*(cnt[size]+1)%P*inv2%P;R int ans=0;for(i=1;i<=size;++i)ans=(ans+(LL)(cnt[i]-cnt[i-1]+P)*a[i])%P;printf("%d\n",ans);return 0;
}

Kod

T3考试的时候没深入推,其实深入推也不会发生更多的事情……
正解尼玛数位dp,这个神奇的special数位dp是去数位dp了Lucas的每一位.
首先,推式子是必须的,无论你是否转换了坐标系,你只要按照组合意义去化简式子,你就得到了一个看起来十分清真的式子.
然后,你发现这个式子是若干个两个组合数相乘的和,利用Lucas以后,是mod进制下每一位的组合数乘积的乘积,因为mod很小,所以我们枚举每一位是什么,f[i][1/0]表示前i位是否需要借位的贡献和,我们最后取f[最高位][0]即可,这样我们保证了我们的枚举到的数一定小于n+m,并且如果不合法,那么他的值为0.

#include <cstdio>
typedef long long LL;
const LL N=200010,P=100003;
LL n,m,jie[P],ni[P],A,B,C,f[5][2];
#define c(n,m) (m>n?0:jie[n]*ni[m]%P*ni[n-m]%P)
int main(){scanf("%lld%lld",&n,&m);A=(n+m)>>1,B=(n-m)>>1,C=A+B;ni[0]=jie[0]=1,ni[P-1]=P-1;LL i,j,num,t,a;for(i=1;i<P;++i)jie[i]=jie[i-1]*i%P;for(i=P-2;i>0;--i)ni[i]=ni[i+1]*(i+1)%P;f[0][0]=1;for(i=1;i<=4;++i){num=C%P,C/=P,a=A%P,A/=P;for(j=0;j<=a;++j){f[i][(t=num-j)<0]=(f[i][t<0]+f[i-1][0]*c(a,j)*c((t+(t<0?P:0)),a))%P;f[i][(--t)<0]=(f[i][t<0]+f[i-1][1]*c(a,j)*c((t+(t<0?P:0)),a))%P;}}printf("%lld\n",f[4][0]);return 0;
}

Kod

笔记:
I.Lucas定理好像除了计算组合数以外,还能将其本身作为解题的工具.
II.推式子的时候式子的美观程度也是很重要的.
III.像这道题一样转换坐标系的方法是值得积累的.
IV.利用组合数本身的性质去刨去卡上界的步骤是很妙的.
V.记录是否借位似乎很妙的样子.
VI.数位dp也可以很活.
本次考试的经验教训:
I.时间分配,速战速决
II.心态良好,临危不乱


生日模拟赛
这场模拟赛,考试4小时,改题一天半……
一开考,日常看一遍题,T1,尼玛样例解释什么鬼,T2,不会,T3,好像有锅,先不做他,好像又是什么全场只有我不会的字符串题.
决定先去搞T1,做了几下,不会,于是开始写暴搜,想起来昨天晚上刚学了叉积,就想用叉积打check,结果,连推带打1h+,调出来出来之后,发现,这个爆搜并不能拿到一点分,于是先让他打着表,去搞T2,推出一个小性质,会了30~40的暴力,码了出来,过了大样例,又回去看T1的表,没什么规律,于是又打了许多其他的表,仍然没有什么规律,于是一脸绝望的我决定交一个表上去,所以我就让他在那里打表,去打T3了.看了看T3,好像只会7分……想起来之前鬼一样的字符串fft,于是开始想fft,虽然没有想到fft怎么打,但是我还是想到了用两个数组对应项相乘的方法,最后发现这个可以推广到全局,于是码出来了一个20分左右的暴力,由于听说数据范围改了,所以我也不知道我能拿多少分,继续对着T3YY,想出来了一个kmp做法,似乎比原来的暴力优秀不少,但是我意识到我并不会打kmp啊,ac自动机也不会打,后缀自动机也忘了,看着考试不剩多少时间,我就先去收了T1的表,然后把T1,T2打理好,交上,然后又开始搞T3,然而到考试结束我也没有把那个kmp码出来……
最后20+40+69=129
考试方面的总结:
I.读题要准,要快.
II.打暴力要快.
III.不要在一个没什么规律的表上耗费太多的时间.
IV.不要对一道题投入太少.
V.要有信仰.
VI.考试的时候一定要冷静,不要把时间复杂度计算错误.
VII.基础算法要掌握牢啊.
T1是个神题.
T2十分巧妙,我的40分暴力就不说了,下一个部分分,也就是70分做法,是推一下式子,发现我们的pos在前后都会满足一个式子,并且结合原来的暴力得知pos是唯一的,于是枚举pos,进行O(n^2)的dp,于是总复杂度为O(n^3).正解是在这个做法的基础之上进行巧妙的问题转化,将我们的贡献计算公式转化为组合问题(染色问题或选取元素问题),从而使得我们本来的瓶颈(我们的限制条件对于pos来说一定要从pos向两边走,从而使得我们的dp不得不每次重新开始)得以解决,也就是说我们只需要进行O(1)次O(n^2)的dp,同时为了方便计算,正解还引入了一些新的定义,最终使得我们可以在O(n^2)的时间内得到答案.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define read(a) (scanf("%d",&a))
typedef long long LL;
const int N=2010;
const int P=1000000007;
int n,ans,a[N];
int f0[N][N],f1[N][N],g0[N][N],g1[N][N],g2[N][N];
inline void Init(){read(n);register int i;for(i=1;i<=n;++i)read(a[i]);f0[0][0]=1;g0[n+1][0]=1;
}
inline void dp(){register int i,j,k;for(i=1;i<=n;++i)for(j=0;j<=i;++j){if(a[i]){k=std::max(2,j+1);f0[i][k]=(f0[i][k]+f0[i-1][j])%P;f1[i][k]=(f1[i][k]+f1[i-1][j])%P;}if(a[i]<=0){k=std::max(0,j-1);f0[i][k]=(f0[i][k]+f0[i-1][j])%P;f1[i][k]=((LL)f1[i][k]+f0[i-1][j]+f1[i-1][j])%P;}}for(i=n;i>0;--i)for(j=0;j<=(n-i);++j){if(a[i]){k=std::max(0,j-1);g0[i][k]=(g0[i][k]+g0[i+1][j])%P;g1[i][k]=((LL)g1[i][k]+g0[i+1][j]+g1[i+1][j])%P;g2[i][k]=((LL)g2[i][k]+g1[i+1][j]*2+g2[i+1][j]+g0[i+1][j])%P;}if(a[i]<=0){k=j+1;g0[i][k]=(g0[i][k]+g0[i+1][j])%P;g1[i][k]=(g1[i][k]+g1[i+1][j])%P;g2[i][k]=(g2[i][k]+g2[i+1][j])%P;}}
}
inline void print(){for(register int i=0;i<=n;++i)ans=(ans+(LL)f0[i][0]*g2[i+1][0]+(LL)f1[i][0]*g1[i+1][0])%P;printf("%d\n",ans);
}
int main(){Init(),dp(),print();return 0;
}

Kod

笔记:
I.又是一次找限制条件,我又没发现,从限制条件入手和发掘题目信息似乎有点像,都很重要啊.
II.我的dp好弱啊……
III.找准问题瓶颈在一定程度上有利于解题.
IV.问题转化,尤其是将抽象问题转化为组合问题,似乎很妙的样子.
V.对于dp状态,引入新的定义似乎会带来新的方向.
T3这题的数据真是水……作为一只字符串弱鸡,拿到这些分数已经很庆幸了……正解据说的SAM,但是由于题解很唬,std更唬,聪明的OIer们就自己YY了另外一种解法,不知道和正解比起来谁更优秀,总之是一种时空复杂度应该都合法的做法,反正在现有数据范围下跑得挺快.我的思路,和我后来想的kmp,以及看到题解之后YY的SAM,都无法避免每一个贡献都切切实实得去给一个O(1)的时间,这样,时间复杂度都和我的暴力是一样的.但是呢,wxh想到了用二分+hash来一次跑多个贡献,但是由于串数问题,还是过不了,不过zzh提到了Trie树,然后zyf提议Trie树hash,所以就有了一种O(n*logn*hash_table)时间复杂度的做法,感觉很优秀,于是所有人都用这种方法过掉了这题,正解凉凉了……
一个字符串算法的板子都不会,真是够了……
马上就要省选了,我的字符串水平很着急啊……

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
typedef unsigned long long ULL;
const int N=100010,M=300010,P=1000000007,mod=612497;
const ULL K=199;
struct Hash_Table{struct Link{int next,key;ULL hash;}link[M];int head[mod],t;void insert(ULL hash,int key){int pos=hash%mod;link[++t].hash=hash;link[t].key=key;link[t].next=head[pos];head[pos]=t;}bool count(ULL hash){for(register int i=head[hash%mod];i;i=link[i].next)if(link[i].hash==hash)return true;return false;}int give(ULL hash){for(register int i=head[hash%mod];i;i=link[i].next)if(link[i].hash==hash)return link[i].key;}
}hash_table;
struct Trie{Trie *ch[5];int cnt;
}node[M],*root;
int sz;
#define newnode (node+(sz++))
inline void Init(){root=newnode;}
char z[N],t[M];
int n,m,a[N],b[N];
ULL zi[N],fi[N],cf[N];
inline char check(char ch){switch(ch){case 'A':return 1;case 'C':return 2;case 'G':return 3;case 'T':return 4;}
}
inline void insert(char *s){register int i,l=strlen(s+1);register Trie *p=root;for(i=1;i<=l;++i){s[i]=check(s[i]);if(!p->ch[s[i]])p->ch[s[i]]=newnode;p=p->ch[s[i]],++p->cnt;}
}
inline void dfs(Trie *p,int sum,ULL hash){if(!p)return;sum+=p->cnt;if(sum)hash_table.insert(hash,sum);for(int i=1;i<=4;++i)dfs(p->ch[i],sum,hash*K+i);
}
#define get_hash(a,b,c) (a[c]-a[b-1]*cf[c-b+1])
inline void full_in(int *ai,ULL *ki){register int i,l,r,mid,ans;for(i=1;i<=n;++i){l=i,r=n,ans=0;while(l<=r){mid=(l+r)>>1;if(hash_table.count(get_hash(ki,i,mid)))ans=mid,l=mid+1;else r=mid-1;}if(ans)ai[i]=hash_table.give(get_hash(ki,i,ans));}
}
int main(){Init();scanf("%s",z+1);n=strlen(z+1);register int i,ans=0;for(i=1;i<=n;++i)z[i]=check(z[i]),zi[i]=zi[i-1]*K+z[i];cf[0]=1;for(i=1;i<=n;++i)fi[i]=fi[i-1]*K+z[n-i+1],cf[i]=cf[i-1]*K;scanf("%d",&m);for(i=1;i<=m;++i)scanf("%s",t+1),insert(t);dfs(root,0,0);full_in(a,zi),full_in(b,fi);for(i=1;i<=n;++i)ans=(ans+(LL)a[i]*b[n-i+2])%P;printf("%d\n",ans);return 0;
}

Kod

笔记:
I.哈希Trie树好像很优秀的样子,但是我还不是特别清楚这玩意是啥,好像是用来查询一个串是否是Trie树前缀的?
II.找到问题瓶颈,并以其为方向去思考,并努力解决他,是一个不错的解决问题的方式.
III.hash的时候模数的选取影响效果,进制数的选取仍然影响效果.
IV.字符串匹配问题上数组也是一种不错的方法.


翻车模拟赛
日常先看一遍题.T1,额,支配树?T2,多项式?ln?根号?T3,额,读了好长时间才读懂题,码农题……
回到T1,仔细端详,诶?成都集训的时候有这道题?好像还是YJQ讲的,额,回忆一下,好像有什么反向Inf边,自己YY了一下,然后手玩了一下,又猜了一发,证了一下,码了出来,过了样例,感觉一切十分完美.在我离开这题的前一秒,突然注意到有不合法的情况,怎么判呢?直接判最大流是否大于Inf就好了!迅速改完交上,去看T2.这啥玩意啊,干,好像会个10分……看了一下表,还剩2.5h+,这个码农题,应该还能干.于是去搞T3,想了一下LCT维护子树,一开始我因为看到题目的描述很像LCT的过程,就一直想怎么在按照他的过程走的时候计算答案,发现这样似乎不太可行,于是我就去想用LCT维护子树来记录我想记录的所有信息,发现这太屎了……于是我结合刚刚想到的东西,YY出来一种比较好打的把原问题拆成两部分的做法,第一部分,沿用我一开始的思路,一边跟着走,一边统计子树信息,第二部分,按照第二次想的思路,不过不用维护子树,去计算链的贡献.于是决定先码个暴力出来,然后去码刚刚的思路,就这样码了1h+,随后,用1h-调试,最终在离考试结束还剩10min的时候,拍上了.交上T3之后迅速去码T2小暴力,在离考试结束还剩1min的时候,过了小样例,赶紧交上,然后测了一发大样例,没过……
最后10+0+100=110
T1,我TM在迅速码上判断无解的部分的时候,忘了改原来的输出了,结果跑了两边dinic,只拿到了无解的分,这和直接输出-1是一个分啊,凑.
以前我都是强迫症一样地小心,最近好不容易改过来了,反而成了大意???看来在小心谨慎的程度方面,还是要有个度的,我要是改完之后,测一发样例,也不至于10分啊.幸好这不是省选,长个教训,在确定最终提交程序之前,先测试一下啊.
不过,回到这道题本身来,要不是以前考过,我肯定不会啊,像这道题这样的最小割加Inf边,加反向边的思路,我还是没有掌握好的,省选之前,一定要抽时间搞搞网络流啊.
我的大意好像还和一时兴奋有些关系,以后考试的时候一定要时刻冷静,不慌,也不要兴奋.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define read(a) (scanf("%d",&a))
typedef long long LL;
const int N=110;
const int M=2510;
const LL Inf=1e17;
struct V{int to,next;LL f;
}c[M<<2];
int head[N],t=1;
inline void add(int x,int y,LL z){c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].f=z;
}
int q[N],front,back;
int deep[N];
int n,m;
inline bool bfs(){memset(deep,-1,sizeof(deep));deep[1]=1;front=back=0;q[back++]=1;register int i,x;while(front!=back){x=q[front++];for(i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==-1){deep[c[i].to]=deep[x]+1;if(c[i].to==n)return true;q[back++]=c[i].to;}}return false;
}
inline LL dfs(int x,LL v){if(x==n||v==0)return v;register LL ret=0,f;for(int i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==deep[x]+1){f=dfs(c[i].to,std::min(c[i].f,v));ret+=f,v-=f,c[i].f-=f,c[i^1].f+=f;if(!v)break;}if(!ret)deep[x]=-1;return ret;
}
inline LL dinic(){LL ret=0;while(bfs())ret+=dfs(1,Inf);return ret;
}
int main(){read(n),read(m);register int i,x,y,z;for(i=1;i<=m;++i){read(x),read(y),read(z);add(x,y,z),add(y,x,Inf);}LL ans=dinic();if(ans>=Inf)puts("-1");else printf("%lld\n",ans);return 0;
}

Kod

T2,我的暴力是错的,但是我看到的那个10分,其实是考察的对n==1的特判啊!
这道题把问题转化为了二分图,是啊,这两个问题完全等价啊,又是问题的等价转化,我尼玛又不会!!!
以后一定要特别地注意模型转化的思路.
转为二分图以后,就是比较普通的图论组合数dp——增量构造的同时,钦定固定点,枚举固定点所在联通块大小,进行dp转移.换个角度来看的话,这其实应该是比较普通的求联通图dp值的容斥.
特判拿分不失为一种好策略.
时间短促的时候,想几个有理有据的特判,不失为一种好策略,不过还是要切合具体情况和写小暴力衡量一下.

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=1000;
const int P=105225319;
const int inv2=52612660;
int n,m,mi[N*N+10],c[N+10][N+10],g[N+10],f[N+10],ans[N+10];
int main(){scanf("%d%d",&n,&m);if(n==1){puts("0");return 0;}register int i,j;c[0][0]=1;for(i=1;i<=n;++i){c[i][0]=1;for(j=1;j<=i;++j)c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;}mi[0]=1,++m,j=n*n;for(i=1;i<=j;++i)mi[i]=(LL)mi[i-1]*m%P;g[0]=f[0]=ans[0]=1,f[1]=2;for(i=1;i<=n;++i)for(j=0;j<=i;++j)g[i]=(g[i]+(LL)c[i][j]*mi[(i-j)*j])%P;for(i=2;i<=n;++i){for(j=1;j<i;++j)f[i]=(f[i]+(LL)c[i-1][j-1]*f[j]%P*g[i-j])%P;f[i]=(g[i]-f[i]+P)%P;}for(i=1;i<=n;++i){f[i]=(LL)f[i]*inv2%P;for(j=1;j<=i;++j)ans[i]=(ans[i]+(LL)c[i-1][j-1]*f[j]%P*ans[i-j])%P;}printf("%d\n",ans[n]);return 0;
}

Kod

T3,只要知道一些LCT就很好想,有一些小坑的码农题,据说jcy有用一个LCT解决的办法,但是似乎很屎,不如写俩(其实是,写一个,粘一个).
发现是bzoj原题——bzoj3779:重组病毒.

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=100010;
struct V{int to,next;}c[N<<1];
int head[N],t;
LL ans1[N];
int n,m,ans2[N],ans3[N],opt[N],poi[N];
char s[100];
inline void add(int x,int y){c[++t].to=y,c[t].next=head[x],head[x]=t;
}
namespace work1{struct splay{splay *ch[2],*f;int rev;int size_of_void_son;LL ans_of_void_son;int size_of_all_son;LL ans_of_all_son;inline void pushup(){size_of_all_son=size_of_void_son+ch[0]->size_of_all_son+ch[1]->size_of_all_son;ans_of_all_son=ans_of_void_son+ch[0]->ans_of_all_son+ch[1]->ans_of_all_son;}inline void mark(){rev^=1;std::swap(ch[0],ch[1]);}inline void pushdown(){if(!rev)return;ch[0]->mark();ch[1]->mark();rev=0;}}*null,node[N];inline int isroot(splay *p){return p->f->ch[0]!=p&&p->f->ch[1]!=p;}inline int get(splay *p){return p->f->ch[1]==p;}inline void dig(splay *p){if(isroot(p)){p->pushdown();return;}dig(p->f);p->pushdown();}inline void rotate(splay *p){splay *fa=p->f,*pa=fa->f;int j=get(p);if(!isroot(fa))pa->ch[get(fa)]=p;if((fa->ch[j]=p->ch[j^1])!=null)fa->ch[j]->f=fa;fa->f=p,p->ch[j^1]=fa,p->f=pa;fa->pushup();p->pushup();}inline void spaly(splay *p){dig(p);for(splay *fa=p->f;!isroot(p);rotate(p),fa=p->f)if(!isroot(fa))rotate(get(fa)==get(p)?fa:p);}inline void expose(splay *x){splay *y=null;while(x!=null){spaly(x);x->size_of_void_son-=y->size_of_all_son;x->ans_of_void_son-=y->ans_of_all_son+y->size_of_all_son;y->pushup();std::swap(x->ch[1],y);x->size_of_void_son+=y->size_of_all_son;x->ans_of_void_son+=y->ans_of_all_son+y->size_of_all_son;y=x;x=x->f;}y->pushup();}inline void make_root(splay *p){expose(p),spaly(p),p->mark();}inline void dfs(int x,int fa){node[x].f=node+fa;node[x].ch[0]=node[x].ch[1]=null;node[x].size_of_void_son=1;node[x].ans_of_void_son=1;for(int i=head[x];i;i=c[i].next)if(c[i].to!=fa){dfs(c[i].to,x);node[x].size_of_void_son+=node[c[i].to].size_of_all_son;node[x].ans_of_void_son+=node[c[i].to].ans_of_all_son+node[c[i].to].size_of_all_son;}node[x].pushup();}inline void Init(){null=node;null->ch[0]=null->ch[1]=null->f=null;dfs(1,0);}inline void Main(){Init();register int i,x;splay *p;for(i=1;i<=m;++i){switch(opt[i]){case 1:expose(node+poi[i]);break;case 2:make_root(node+poi[i]);break;case 3:p=node+poi[i],spaly(p);ans1[i]=p->ch[1]->ans_of_all_son+p->ans_of_void_son;ans2[i]=p->ch[1]->size_of_all_son+p->size_of_void_son;break;}}}
}
namespace work2{struct splay{splay *ch[2],*f;int color_mark,rev_mark;int my_color;int left_color,right_color,color_size;inline void pushup(){left_color=ch[0]->color_size?ch[0]->left_color:my_color;right_color=ch[1]->color_size?ch[1]->right_color:my_color;color_size=1;if(ch[0]->color_size){color_size+=ch[0]->color_size;if(ch[0]->right_color==my_color)--color_size;}if(ch[1]->color_size){color_size+=ch[1]->color_size;if(ch[1]->left_color==my_color)--color_size;}}inline void mark_rev(){rev_mark^=1;std::swap(ch[0],ch[1]);std::swap(left_color,right_color);}inline void mark_color(int color){my_color=color;left_color=right_color=color;color_size=color_size?1:0;color_mark=color;}inline void pushdown(){if(rev_mark){ch[0]->mark_rev();ch[1]->mark_rev();rev_mark=0;}if(color_mark){ch[0]->mark_color(color_mark);ch[1]->mark_color(color_mark);color_mark=0;}}}*null,node[N];int cnt_color;inline int isroot(splay *p){return p->f->ch[0]!=p&&p->f->ch[1]!=p;}inline int get(splay *p){return p->f->ch[1]==p;}inline void dig(splay *p){if(isroot(p)){p->pushdown();return;}dig(p->f);p->pushdown();}inline void rotate(splay *p){splay *fa=p->f,*pa=fa->f;int j=get(p);if(!isroot(fa))pa->ch[get(fa)]=p;if((fa->ch[j]=p->ch[j^1])!=null)fa->ch[j]->f=fa;fa->f=p,p->ch[j^1]=fa,p->f=pa;fa->pushup();p->pushup();}inline void spaly(splay *p){dig(p);for(splay *fa=p->f;!isroot(p);rotate(p),fa=p->f)if(!isroot(fa))rotate(get(fa)==get(p)?fa:p);}inline void expose(splay *x){splay *y=null;while(x!=null){spaly(x);x->ch[1]=y;x->pushup();y=x;x=x->f;}}inline void make_root(splay *p,int color){expose(p),spaly(p),p->mark_rev(),p->mark_color(color);}inline void dfs(int x,int fa){node[x].f=node+fa;node[x].ch[0]=node[x].ch[1]=null;node[x].my_color=node[x].left_color=node[x].right_color=++cnt_color;node[x].color_size=1;for(int i=head[x];i;i=c[i].next)if(c[i].to!=fa)dfs(c[i].to,x);}inline void Init(){null=node;null->ch[0]=null->ch[1]=null->f=null;dfs(1,0);}inline void Main(){Init();register int i,x;splay *p;for(i=1;i<=m;++i){p=node+poi[i];switch(opt[i]){case 1:expose(p),spaly(p);p->mark_color(++cnt_color);break;case 2:make_root(p,++cnt_color);break;case 3:expose(p),spaly(p);ans3[i]=p->color_size-1;break;}}}
}
int main(){scanf("%d%d",&n,&m);int i,x,y;for(i=1;i<n;++i){scanf("%d%d",&x,&y);add(x,y),add(y,x);}for(i=1;i<=m;++i){scanf("%s",s);switch(s[2]){case 'L':opt[i]=1;break;case 'C':opt[i]=2;break;case 'Q':opt[i]=3;break;}scanf("%d",&poi[i]);}work1::Main(),work2::Main();for(i=1;i<=m;++i)if(opt[i]==3){printf("%.10f\n",(double)ans1[i]/ans2[i]+ans3[i]);}return 0;
}

Kod


UOJ Round #17
第一次打UOJ,没怎么上心,全程划水……
T1,对着部分分码了一发贪心,结果过了所有样例,出题人在逗我?不管了,就这样吧.
T2,额,啥玩意,暴力怎么打,为什么会出现不在点上的情况……
T3,想了好久,仍然只会10分……
考试结束之前,去码了一个瞎搞的T2暴力……
最后,25+5+10=40分……
T1的贪心是错的,给一个样例:
5
11 12 13 15 19
看完样例就能知道为什么是错的了,大该意思可以感性理解为,一开始选择较大,但是1的位对于之后的数来说不“亲切”,结果导致最后答案更优.
正解当然不是什么贪心,是dp啦.
首先,我们发现我们的过程实际上就是把这些数的非公共1位逐步变成0,那么我们可以先把公共1位提出来,对于非公共1位的每个状态,算出达到其的最小花费,但是使用了重复数字怎么办?不可能啊,我们要的是最小花费啊.这样我们有了55分做法.加上之前的贪心,就70了!
然后,我们考虑优化,我们可以对于每次转移不使用枚举元素,而是预处理空集状态是否存在然后枚举子集,这样,再加上一些减枝就可以过了.
在这道题里,我们要善于发现dp的正确性,以及贪心的错误,这个只需要仔细思考,就像jiry_2说的“事实上的确是希望考验大家独立思考、小心求证的能力”.同时,简洁高效得实现代码也是在打这道题的贪心的时候得到的教训,明明每次sort就可以,我却打了堆,慢了不少(这个堆的常数不小嘛,好像插入和查询没什么常数,而删除的常数是3).在找出贪心的错误的时候,目的性地去构造数据,感觉是一个不错的方法,少了许多抽象的证明,但是感觉不是总是适用.在后来优化的过程中,发现转移的实质以及允许以优充劣的情况的出现,是这道题体现的好的思想.

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS=xB,*xT=xB;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){register char ch=gtc;for(x=0;ch<'0'||ch>'9';ch=gtc);for(;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=gtc);
}
typedef long long LL;
const int A=18;
const LL Inf=1e16;
const int N=100010;
const int F=(1<<18)+10;
int bin[A],pub,all;
int n,a[N],m,st[F],ex[F];
LL ans,f[F];
int main(){read(n),pub=(1<<A)-1;register int i,j,s;for(i=1;i<=n;++i)read(a[i]),pub&=a[i],all|=a[i];ans=(LL)pub*n;bin[0]=1;for(i=1;i<A;++i)bin[i]=bin[i-1]<<1;ex[0]=1;all^=pub;for(i=1;i<=n;++i)ex[(a[i]&all)^all]=1;for(i=all;i;i=(i-1)&all){if(ex[i])continue;for(j=0;j<A;++j)if((all&bin[j])&&(i&bin[j])==0)ex[i]|=ex[i|bin[j]];}for(i=all;i;i=(i-1)&all)st[++m]=i;f[0]=0;for(i=m;i>0;--i){s=st[i],f[s]=Inf;for(j=s;j;j=(j-1)&s){if(f[s]<=(s^j))break;if(ex[j])f[s]=std::min(f[s],f[s^j]+(s^j));}}ans+=f[all];printf("%lld\n",ans);return 0;
}

Kod

T2,就是仔细分析一下题目中描述的运动,设计合理的状态,然后二分+bfs或者直接spfa来解决它,而分析这个运动以及设计合理状态,就是这道题的关键所在了,这也需要一定的证明正确性,以及意识到错误的能力.我在分析题目和设计状态的方面还是比较弱的.

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ft first
#define sd second
#define mmp(a,b) (std::make_pair(a,b))
typedef long long LL;
typedef std::pair<int,int> pii;
const int N=1010;
const double Inf=1e7;
const double eps=1e-6;
double dis[N][N],Dis[N][N];
bool fin[N][N];
struct V{int to,next,id;
}c[N<<1];
int head[N],t;
inline void add(int x,int y,int z){c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].id=z;
}
int n,sx,sy;
int a[N],b[N];
int degree[N];
pii queue[N*N],e[N];
int front,back;
bool in[N][N];
inline double query(int i,int x,int y){double ret=std::min(dis[i][x],dis[i][y]);double tx,ty,ki,bi,kei,bei;if(b[x]==b[y])tx=a[i],ty=b[x];else if(a[x]==a[y])tx=a[x],ty=b[i];else{ki=(double)(a[y]-a[x])/(b[x]-b[y]);bi=b[i]-a[i]*ki;kei=(double)(b[x]-b[y])/(a[x]-a[y]);bei=b[x]-a[x]*kei;tx=(bi-bei)/(kei-ki);ty=tx*ki+bi;}if((a[x]<=tx&&tx<=a[y])||(a[y]<=tx&&tx<=a[x]))ret=std::sqrt((tx-a[i])*(tx-a[i])+(ty-b[i])*(ty-b[i]));return ret;
}
inline bool check(int x,int y,double lim){return (fin[x][e[y].ft]&&dis[x][e[y].ft]<=lim)||(fin[x][e[y].sd]&&dis[x][e[y].sd]<=lim);
}
inline bool check(double lim){front=back=0;memset(in,0,sizeof(in));register int x,y,i,u,w,q;for(i=head[sx];i;i=c[i].next)if(Dis[sy][c[i].id]<=lim){in[sy][c[i].id]=true;queue[back++]=mmp(sy,c[i].id);}for(i=head[sy];i;i=c[i].next)if(Dis[sx][c[i].id]<=lim){in[sx][c[i].id]=true;queue[back++]=mmp(sx,c[i].id);}while(front!=back){x=queue[front].ft,y=queue[front].sd;front++;w=e[y].ft,q=e[y].sd;for(i=head[x];i;i=c[i].next){u=c[i].to;if(in[u][y]==false&&Dis[u][y]<=lim){if(check(u,y,lim))return true;in[u][y]=true;queue[back++]=mmp(u,y);}u=c[i].id;if(in[w][u]==false&&Dis[w][u]<=lim){if(check(w,u,lim))return true;in[w][u]=true;queue[back++]=mmp(w,u);}if(in[q][u]==false&&Dis[q][u]<=lim){if(check(q,u,lim))return true;in[q][u]=true;queue[back++]=mmp(q,u);}}}return false;
}
inline double judge(){double l=dis[sx][sy],r=Inf,mid;while(l+eps<r){mid=(l+r)*0.5;if(check(mid))r=mid;else l=mid;}return r;
}
int main(){//freopen("rio.in","r",stdin);scanf("%d%d%d",&n,&sx,&sy);register int i,j,x,y;for(i=1;i<=n;++i)scanf("%d%d",&a[i],&b[i]);for(i=1;i<n;++i){scanf("%d%d",&x,&y);add(x,y,i),add(y,x,i);++degree[x],++degree[y];e[i]=mmp(x,y);}for(i=1;i<=n;++i)for(j=1;j<=n;++j){dis[i][j]=std::sqrt((LL)(a[i]-a[j])*(LL)(a[i]-a[j])+(LL)(b[i]-b[j])*(LL)(b[i]-b[j]));fin[i][j]=(degree[i]==1&&degree[j]==1)||(i==j);}if(sx==sy){puts("0");return 0;}if(fin[sx][sy]){printf("%.10f\n",dis[sx][sy]);return 0;}for(i=1;i<=n;++i)for(j=1;j<n;++j)Dis[i][j]=query(i,e[j].ft,e[j].sd);printf("%.10f\n",judge());return 0;
}

Kod

T3,还没有去搞正解,只能说知道这题在问啥吧.正解是积分,我的积分还是很弱,在这道题里,除了打了一个傻暴力以外,最大的收获就是学了一发神奇的随机化.学到的地方大概是:多次随机去计算期望值,枚举分数来匹配小数从而解决答案在模意义下的问题,调整精度来调整随机化结果,一个神奇的随机数写法.
对于这道题,我们的答案是模意义下的,感觉不太能随机化,但是感觉起来是个分数,而且分子分母不会太大(至于为什么,我也不清楚),那么我们可以先随机化小数,然后再枚举分数,去找距离随机化结果最近的分数,然后输出.这个东西在n,m比较大的时候就不适用了,但是最起码可以骗到30分啊.
在随机化的时候,随机数函数的写法十分值得学习,而且一开始我把0x7fffffff写成0x7f7f7f7f导致随机小数十分不准……
感觉这个随机数函数写法十分随机,基本找不到重复数字……

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define read(a) (scanf("%d",&a))
namespace RAND{int seed=45,put_out=0x7fffffff;inline double rando(){seed^=seed<<5;seed^=seed>>17;seed^=seed<<13;return (double)(seed&=put_out)/put_out;}
}
typedef long long LL;
const int N=30;
const int P=998244353;
const int T=10000000;
const int L=100;
const double eps=1e-5;
int n,m,a[N*N],b[N*N],inv[L+10];
double f[N];
int main(){read(n),read(m);register int i,j;inv[1]=1;for(i=2;i<=L;++i)inv[i]=(-(LL)P/i*inv[P%i]%P+P)%P;if(n>10){printf("%lld\n",(LL)(3*n-2)*inv[2*n]%P);return 0;}for(i=1;i<=m;++i)read(a[i]),read(b[i]);register double sum=0.,max;for(i=1;i<=T;++i){max=0.;for(j=1;j<=n;++j)f[j]=RAND::rando(),max=std::max(max,f[j]);for(j=1;j<=m;++j)max=std::max(max,f[a[j]]+f[b[j]]);sum+=max;}sum/=T;double min=1e16;int ans;for(i=1;i<=(L<<1);++i){for(j=1;j<=L;++j)if(std::fabs((double)i/j-sum)<min){min=std::fabs((double)i/j-sum);ans=(LL)i*inv[j]%P;if(min<eps)break;}if(min<eps)break;}printf("%d\n",ans);return 0;
}

Kod


No.18省选模拟赛
一开考,日常先看一遍题,T1,贪心?T2,哈夫曼编码?T3,啥玩意?
决定先去开T1,感觉这个贪心不是很难啊,为了卡常,打了斜堆,码了出来,发现事情很不对,woc,我是错的,于是陷入沉思,把第一部分贪心打对,但是第二部分贪心死活不对啊,然后在发现时间不够之后,瞎打了一发,不管了.开始干T2,好像我需要在Trie上建2-sat,但是这似乎只是50分做法啊,不管了,先码,码完之后,一路静态查错,发现错误好多啊,等我把静态查错完成,时间已经不太够了,幸好直接过了样例.然后T3写了一个输出-1.
最后20+80+12=112分.
T1,我感觉我要是当时早点从这深渊中脱离,去打一个状压,兴许还能是一个O(n^2)dp,就能多拿一些分数,而且还会剩下很多时间,切题上瘾的毛病最近又犯了,合理分配时间,尽量拿高分才是重中之重啊!
出此之外,想好了再打,在大多数情况下都是一个比较好的选择,没想好去打,在某些情况下也不是不可以.
我的题解http://www.cnblogs.com/TSHugh/p/8625121.html
T2,我的做法由于HZOI上没有包而拿到了80分……然而,实际上,有两种特殊情况我没有判,少拿了8分,还是思维不严谨……这道题的栈空间好像还有点迷……
清醒地去静态查错确实比无脑对拍,要高效得多,但是也有一些想不到的情况,不用对拍是查不出来的.
我的题解http://www.cnblogs.com/TSHugh/p/8622572.html
T3,无论如何都不要放弃梦想……
我的题解http://www.cnblogs.com/TSHugh/p/8625433.html


math模拟赛
三道数学题……
日常先看一遍题,T1,多项式?还是分三瓣的?T2,数位dp,矩阵乘法?T3,这题,好水,继续往下翻,干,模数不是质数……
先搞T3,前几天做了一道poi的题,模数也不是质数,我用堆搞过去的,于是我开始尝试用堆搞这道题,推了推,发现可以,于是开始码,码完静态查错,过了所以样例,写暴力对拍,拍出了错,结果是暴力写错了……开始怼T2,怒怼1h+之后,还是只会10分,感觉是什么dp,维护循环节的,不太会……于是赶紧弃坑去T1,先写出30分暴力,然后想到了另一个可以和30分拼起来的暴力,迅速写出,不过差了11s,没有交上……
最后30+10+100=140分
T1,我那个差11s没交上的暴力可以多拿10分……
和我想我那个多10分的暴力一样,50分做法的出发点也是第二个部分分里m很小,50分做法用到了一个小性质,相同权值的点一定在一起(证明很简单:如果不是的话,一定可以把相同权值的点移在一起使答案变小),那么我们就可以直接枚举哪些权值出现了,然后把每一种权值的点看作一个点(正确性很好理解),这样的话,再加上之前的30分dfs就可以拿到50分了.
100分的话,继续沿用50分的思路,把点分成若干联通块.这次我们肯定不能像之前那样枚举权值,我们这次以最高位上是否有1,把点分为两部分,这两部分点一定内部联通,之间有一条最小的边(证明同50分做法).那么我们就会得到一种状态f,一种转移,但是我们发现我们转移的时候出现了一些奇奇怪怪的东西,g,求g的话,就不像f那样好求了,我们需要一些设计dp状态与转移的技巧,我们可以设计后缀意义的dp状态,并且用后缀方案数累加得到总和,这就有了g的转移,以及p的出现,而p的出现,可以沿用我们一开始的思路进行转移.这样这道题就结束了,只是还有一些dp转移的时候的一些小细节,以及,有一个必要的对于p的剪枝,具体细节见Kod.
这道题的话,在部分分的启发性方面是很不错的,还有这个dp套dp套dp的用法真是不用dfs打不出来额,还有,里面dp的一些小细节还真是要命,一定要清楚我们dp的时候会出现的细节情况以及边界条件,这样才有利于在dp的时候,不漏掉任何细节.
在常数方面的话,少模还是挺厉害的,另外预处理一些东西也会带来一些常数上的优化.但是,再怎么常数优化,也不如好好地剪一下无用状态,利用dfs里的转移的样子推断一下怎么剪才强有力.所以说,dfs->剪枝这条有向边不仅有理有据还很很普适.
不断地观察性质,利用性质,强化性质,也是一种从0到100的途径嘛.
数位上高位与低位之间的绝对优势,是一种不错的解题思路.
从一开始的dp状态与转移,不断地推出新的dp状态与转移,不失为一种好的方法.

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=55;
const int M=10;
const int FL=260;
const int P=258280327;
inline int Pow(int x,int y){int ret=1;while(y){if(y&1)ret=(LL)ret*x%P;x=(LL)x*x%P,y>>=1;}return ret;
}
int f[N][M],g[N][N][M],h[N][N][M][FL],bin[N*N],c[N][N],cnm[N][N][N][N];
inline void Init(){int i,j,k,l;bin[0]=1;for(i=1;i<N*N;++i)bin[i]=(bin[i-1]<<1)%P;c[0][0]=1;for(i=1;i<N;++i){c[i][0]=1;for(j=1;j<=i;++j)c[i][j]=(c[i-1][j-1]+c[i-1][j])%P;}for(i=1;i<N;++i)for(j=0;j<=i;++j)for(l=1;l<N;++l)for(k=0;k<=l;++k)cnm[i][j][l][k]=(LL)c[i][j]*c[l][k]%P;memset(f,-1,sizeof(f));memset(g,-1,sizeof(g));memset(h,-1,sizeof(h));for(i=0;i<M;++i)f[0][i]=f[1][i]=0;for(i=2;i<N;++i)f[i][0]=0;for(i=0;i<N;++i)for(j=0;j<N;++j){g[i][j][0]=0;for(k=1;k<FL;++k)h[i][j][0][k]=0;}for(i=0;i<N;++i)for(j=0;j<M;++j)g[i][0][j]=g[0][i][j]=0;for(i=0;i<M;++i)for(j=0;j<FL;++j)if(bin[i]-1<j)for(l=0;l<N;++l)for(k=0;k<N;++k)h[l][k][i][j]=0;
}
inline int H(int s,int t,int m,int k){if(h[s][t][m][k]!=-1)return h[s][t][m][k];int i,j;LL ret=0;for(i=0;i<=s;++i){for(j=0;j<=t;++j){ret+=(i==0&&j==t)||(i==s&&j==0)?(bin[m-1]>=k?bin[(s+t)*(m-1)]:H(s,t,m-1,k-bin[m-1])):(LL)cnm[s][i][t][j]*((i!=0&&j!=0)?H(i,j,m-1,k):bin[(i+j)*(m-1)])%P*((i!=s&&j!=t)?H(s-i,t-j,m-1,k):bin[(s-i+t-j)*(m-1)]);}ret%=P;}return h[t][s][m][k]=h[s][t][m][k]=ret;
}
inline int G(int s,int t,int m){if(g[s][t][m]!=-1)return g[s][t][m];int i,ret=0,full=bin[m]-1;for(i=1;i<=full;++i)ret=(ret+H(s,t,m,i))%P;return g[t][s][m]=g[s][t][m]=ret;
}
inline int F(int n,int m){if(f[n][m]!=-1)return f[n][m];int i,ret=0;for(i=0;i<=n;++i)ret=(ret+(LL)c[n][i]*((LL)F(i,m-1)*bin[(m-1)*(n-i)]%P+(LL)F(n-i,m-1)*bin[(m-1)*i]%P+G(i,n-i,m-1)+(i==0||i==n?0:bin[(n+1)*(m-1)])))%P;return f[n][m]=ret;
}
int main(){Init();int n,m;scanf("%d%d",&n,&m);printf("%lld\n",(LL)F(n,m)*Pow(bin[n*m],P-2)%P);return 0;
}

Kod

T2,我想得偏了,那个1e10的数据范围,挺杜教筛的嘛.
首先我们要发现一些东西,就是,题目中的数字个数就是没有循环节的数的个数(循环同构)(可以通过证明二者的充要条件,来证明二者的等价性),这个可以反演,套路性地推一下就可以A掉了.
但是还有一些骚骚的操作,比如说那个求i*10^i的前缀和的时候,我作为一只蒟,去模了一发std,并且以一种神奇的姿势理解了他.后来yzh、wzz纷纷爆出来用矩阵快速幂解决的方法,感觉自己好蒟啊……后来zzh用等比数列直接手推出来那个式子……
我发现我观察性质,发现问题实质,转化模型,推式子的能力还是好弱啊……高考数学没学好啊……
解决问题的出发点很重要啊,出发点不对很危险啊,发现不对赶紧换啊.
突然发现杜教筛是这样的,感觉自己之前理解的不是很好啊.
我觉得我识别矩阵乘法,识别反演的能力差得不行……
考试的时候一定要控制情绪啊,在这道题纠缠太久了……

#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=10000000;
const int P=258280327;
const int inv2=129140164;
const int inv9=229582513;
std::map<LL,int> gg;
char mu[N+10];
int prime[N/10],len,g[N+10];
bool isnot[N+10];
int n;
inline LL Pow(LL x,LL y){LL ret=1;while(y){if(y&1)ret=ret*x%P;x=x*x%P,y>>=1;}return ret;
}
inline int G(LL n){if(n<=N)return g[n];if(gg.count(n))return gg[n];LL ret=0,i,last;for(i=2;i<=n;i=last+1){last=n/(n/i);ret=(ret+((i+last)%P)*((last-i+1)%P)%P*inv2%P*G(n/i))%P;}ret=(1-ret)%P;return gg[n]=ret;
}
inline void Init(){isnot[1]=true,mu[1]=1;register int i,j;for(i=2;i<=N;++i){if(!isnot[i])prime[++len]=i,mu[i]=-1;for(j=1;prime[j]*i<=N;++j){isnot[prime[j]*i]=true;if(i%prime[j]==0){mu[prime[j]*i]=0;break;}mu[prime[j]*i]=-mu[i];}}for(i=1;i<=N;++i)g[i]=(g[i-1]+i*mu[i])%P;
}
inline LL sum(LL n){return (n%P*Pow(10,n)%P-(Pow(10,n)-1)*inv9%P)*inv9%P*10%P;
}
inline LL calc(LL n){LL ret=0,i,last;for(i=1;i<=n;i=last+1){last=n/(n/i);ret=(ret+(LL)(G(last)-G(i-1))*sum(n/i))%P;}return (ret+P)%P;
}
int main(){Init();LL n;scanf("%lld",&n);printf("%lld\n",calc(n));return 0;
}

Kod

T3,看错了数据范围……106看成10^6,不过给106这种数据范围的,就是想让人看错的吧,以后注意一下,不过看到106的话,我更愿意相信他打错了.
实现代码还是不够快,不够准.
这道题用堆解决的方法的介绍:http://www.cnblogs.com/TSHugh/p/8568221.html

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
typedef long long LL;
const int N=1000000;
int prime[N+10],len,min[N+10],mini[N+10];
int mi[N*10],*begin[N+10],size[N+10];
bool isnot[N+10];
int n,P,x,y;
struct Prime{Prime *ch[2],*f;int key,id;inline void pushup(){key=(LL)begin[id][size[id]]*ch[0]->key%P*ch[1]->key%P;} inline void* operator new(size_t);
}*null,*root,node[N+10];
inline void build(Prime *&p,int id,Prime *fa){if(id>len){p=null;return;}p=node+id;p->f=fa;p->id=id;build(p->ch[0],id<<1,p);build(p->ch[1],(id<<1)|1,p);p->pushup();
}
inline int quy(int x,int n){register int ret=0,now=1;while((LL)now*x<=n)++ret,now*=x;return ret;
}
inline void Iput(int x){while(min[x])++size[min[x]],x/=prime[min[x]];
}
inline void update(Prime *p){while(p!=null){p->pushup();p=p->f;}
}
inline void get(int x,int opt){register int ti;while(min[x]){ti=mini[x];size[min[x]]+=opt*ti;update(node+min[x]);while(ti--)x/=prime[min[x]];}
}
inline void Init1(){isnot[1]=true;min[1]=0,mini[1]=0;register int i,j;for(i=2;i<=N;++i){if(!isnot[i])prime[++len]=i,min[i]=len,mini[i]=1;for(j=1;prime[j]*i<=N;++j){isnot[prime[j]*i]=true;min[prime[j]*i]=j;mini[prime[j]*i]=1;if(i%prime[j]==0){mini[prime[j]*i]+=mini[i];break;}}}null=node;null->ch[0]=null->ch[1]=null;null->f=null;null->id=0;null->key=1;
}
inline void Init2(){scanf("%d%d%d%d",&n,&P,&x,&y);x=std::abs(x);y=std::abs(y);if(n-y-x<0){puts("0");exit(0);}if((x&1)!=((n+y)&1)){puts("0");exit(0);}register int i,j,pos=0,lim;for(i=1;i<=n;++i)Iput(i);for(i=1;i<=len;++i){++pos;begin[i]=mi+pos;mi[pos]=1;lim=size[i]+quy(prime[i],n)*2;for(j=1;j<=lim;++j)mi[pos+1]=(LL)mi[pos]*prime[i]%P,++pos;}build(root,1,null);
}
inline void Work(){register int i,ans=0,a,b,c,d;a=0,b=x,c=(n-y-x)>>1,d=(n+y-x)>>1;for(i=1;i<=a;++i)get(i,-1);for(i=1;i<=b;++i)get(i,-1);for(i=1;i<=c;++i)get(i,-1);for(i=1;i<=d;++i)get(i,-1);ans=root->key;while(c&&d){get(c,1),get(d,1);--c,--d;++a,++b;get(a,-1),get(b,-1);ans=(ans+root->key)%P;}printf("%d\n",ans);
}
int main(){//freopen("test.in","r",stdin);//freopen("need.out","w",stdout);//freopen("rio.in","r",stdin);
  Init1();Init2();Work();return 0;
}

Kod

这道题用crt解决的方法的介绍:http://www.cnblogs.com/TSHugh/p/8638050.html

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=1000010;
int n,x,y,mod,P,p,phi,fac[N],num;
inline int Pow(int x,int y){int ret=1;while(y){if(y&1)ret=(LL)ret*x%P;x=(LL)x*x%P,y>>=1;}return ret;
}
inline int cnt(int n){return n?n/p+cnt(n/p):0;
}
inline int sum(int n){return n?((LL)(n/P?Pow(fac[P],n/P):1)*fac[n%P]%P*sum(n/p)%P):1;
}
#define deal(n,a,b) int a=sum(n),b=cnt(n);
inline int C(int n,int m){if(m>n)return 0;deal(n,a0,b0)deal(m,a1,b1)deal(n-m,a2,b2)b0-=b1+b2;if(b0>=num)return 0;return (LL)a0*Pow(a1,phi-1)%P*Pow(a2,phi-1)%P*Pow(p,b0)%P;
}
inline int calc(){int ret=0,i,a,b,c,d;fac[0]=1;for(i=1;i<=P&&i<=n;++i)fac[i]=(i%p)?(LL)fac[i-1]*i%P:fac[i-1];a=0,b=x,c=(n-y-x)>>1,d=(n+y-x)>>1;while(c>=0){ret=(ret+(LL)C(n,a+b)*C(a+b,a)%P*C(c+d,c)%P)%P;++a,++b,--c,--d;}return ret;
}
int main(){scanf("%d%d%d%d",&n,&mod,&x,&y);x=std::abs(x),y=std::abs(y);if(n-y-x<0||((x&1)!=((n+y)&1))){puts("0");return 0;}int i,s=mod,ans=0;for(i=2;s>1;++i){if(i*i>s)i=s;if(s%i==0){p=i,P=1,num=0;while(s%p==0)++num,P*=p,s/=p;phi=P/p*(p-1);ans=(ans+(LL)calc()*(mod/P)%mod*Pow(mod/P,phi-1))%mod;}}printf("%d\n",ans);return 0;
}

Kod


No.21省选模拟赛
开考先看题:T1,01分数规划+网络流?看数据范围需要一些加边的优化.T2,感觉不是很难,但是感觉比较奇怪.T3,啥啊.
先去看一看T1,有点困,不管了,先把暴力码出来,码出来之后,发现有些问题,然后迷迷糊糊地为了正确性而增大了时间复杂度,等我清醒之后,意识到时间不太对,于是先去搞T2了.开始各种推解析几何,各种高考数学……推出来+码出来+调了调=2h+……但是,我突然意识到一种极端情况,而且这种极端情况还是在我手造简单清真小样例的时候见到的,我开始方了……我想去修改,但是时间已经不多了,而且我害怕我还会想到更多更恶心的情况,于是便弃掉他,去搞T3,T3打了一发10分暴力……
最后35+80+10=125分……
T1.啊……以后考试一定不要困了啊,虽然题目描述有误,但是,我明明想到了正解,却因为困,而选择了错误的策略……
所以说,以后考试一定要把状态调整好,什么困啊,饿啊的,都不要发生.
这题的题干就是有毒,描述错了,如果描述没问题,那就一点都不难.
原来4000点200000边跑起网络流来一点都不虚啊.
网络流的加边顺序会成倍得影响程序运行效率,所以说,这玩意要是实在难分析,就本地试一下.
对于一些细节的分析,要快,要准.
题解里,推式子推傻了,但是他那个加两排Inf来去掉负权边的影响的方法(可以用两种方法所得方案的转化来证明其正确性)还是值得学习的.
此外,ryf处理错误题意的方法,还是比较好的,用check下一个数-1e-16的方法来检查等于的情况,比较吊.(但是真的不会渣精度吗……不过有对拍,而且还客可以拼程序,所以考试用这个点子还是不虚的.)

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS,*xT;
#define gtc() (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){register char ch=gtc();bool ud=false;for(x=0;ch<'0'||ch>'9';ch=gtc())if(ch=='-')ud=true;for(;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=gtc());if(ud)x=-x;
}
const int N=4010;
const int M=200010;
const int oo=0x7fffffff;
struct V{int to,next,f;
}c[M<<3];
int head[M+N],t;
inline void add(int x,int y,int z){c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].f=z;c[++t].to=x,c[t].next=head[y],head[y]=t,c[t].f=0;
}
int n,m,e1[M],e2[M],w1[M],w2[N],S,T;
int deep[M+N],queue[M<<1],front,back;
#define poi(a) (a)
#define edge(a) ((a)+n)
inline void Init(){read(n),read(m);int i;for(i=1;i<=n;++i)read(w2[i]);for(i=1;i<=m;++i)read(e1[i]),read(e2[i]),read(w1[i]);S=n+m+1,T=n+m+2;
}
inline bool bfs(){memset(deep,-1,sizeof(deep));front=back=0;queue[back++]=S;deep[S]=0;int x,i;while(front!=back){x=queue[front++];for(i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==-1){deep[c[i].to]=deep[x]+1;if(c[i].to==T)return true;queue[back++]=c[i].to;}}return false;
}
inline int dfs(int x,int v){if(x==T||v==0)return v;int ret=0,f;for(int i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==deep[x]+1){f=dfs(c[i].to,std::min(v,c[i].f));v-=f,ret+=f,c[i].f-=f,c[i^1].f+=f;if(!v)break;}if(!ret)deep[x]=-1;return ret;
}
inline bool check(int x){int ret=0;t=1;int i;memset(head,0,sizeof(head));for(i=1;i<=m;++i){add(S,edge(i),w1[i]);add(edge(i),e1[i],oo);add(edge(i),e2[i],oo);ret+=w1[i];}for(i=1;i<=n;++i)add(poi(i),T,w2[i]*x);while(bfs())ret-=dfs(S,oo);return ret>0;
}
inline void Print(){int l=0,r=10000,mid,ans=0;while(l<=r){mid=(l+r)>>1;if(check(mid))ans=mid,l=mid+1;elser=mid-1;}printf("%d\n",ans);
}
int main(){Init(),Print();return 0;
}

n+m点、n+m边、100分

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS,*xT;
#define gtc() (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){register char ch=gtc();bool ud=false;for(x=0;ch<'0'||ch>'9';ch=gtc())if(ch=='-')ud=true;for(;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=gtc());if(ud)x=-x;
}
const int N=4010;
const int M=200010;
const int oo=0x7fffffff;
struct V{int to,next,f;
}c[(M+(N<<1))<<1];
int head[N],t;
inline void add(int x,int y,int z1,int z2){c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].f=z1;c[++t].to=x,c[t].next=head[y],head[y]=t,c[t].f=z2;
}
int n,m,e1[M],e2[M],w1[M],w2[N],S,T,w3[N];
int deep[N],queue[N],front,back;
inline void Init(){read(n),read(m);int i;for(i=1;i<=n;++i){read(w2[i]);w2[i]<<=1;}for(i=1;i<=m;++i){read(e1[i]),read(e2[i]),read(w1[i]);w3[e1[i]]+=w1[i],w3[e2[i]]+=w1[i];}S=n+1,T=n+2;
}
inline bool bfs(){memset(deep,-1,sizeof(deep));front=back=0;queue[back++]=S;deep[S]=0;int x,i;while(front!=back){x=queue[front++];for(i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==-1){deep[c[i].to]=deep[x]+1;if(c[i].to==T)return true;queue[back++]=c[i].to;}}return false;
}
inline int dfs(int x,int v){if(x==T||v==0)return v;int ret=0,f;for(int i=head[x];i;i=c[i].next)if(c[i].f&&deep[c[i].to]==deep[x]+1){f=dfs(c[i].to,std::min(v,c[i].f));v-=f,ret+=f,c[i].f-=f,c[i^1].f+=f;if(!v)break;}if(!ret)deep[x]=-1;return ret;
}
inline bool check(int x){int ret=0,i;t=1,memset(head,0,sizeof(head));for(i=1;i<=m;++i){add(e1[i],e2[i],w1[i],w1[i]);ret+=w1[i]<<1;}for(i=1;i<=n;++i){add(S,i,w2[i]*x,0);add(i,T,w3[i],0);}while(bfs())ret-=dfs(S,oo);return ret>0;
}
inline void Print(){int l=0,r=5000,mid,ans=0;while(l<=r){mid=(l+r)>>1;if(check(mid))ans=mid,l=mid+1;elser=mid-1;}printf("%d\n",ans);
}
int main(){//freopen("rio.txt","r",stdin);
    Init(),Print();return 0;
}

n点、n+m边、100分

T2,我考试的代码的正确性是有的,只不过有一个小细节,没有判到(思维还是不严谨啊,同时考试的时候一定要确保自己的冷静沉着),但是那对我炸掉的精度来说,已经无关紧要了.
这道题充分证明了,在计算几何中,向量法在精度方面的优秀,以及解析几何法在精度方面的不足,而且在代码复杂度方面,向量法还是优于解析几何法的,不过,同样作为计算几何的基础,二者都是比较重要的吧.
解析几何的话,就是三角函数什么的一顿推.
向量的话,题解上虽然说正解用的是向量法,但是没说怎么用,所以我们用的是yzh的解方程法(还是高考数学啊).大概就是利用几个等式,比如向量的运算(加减法)、距离相等(到圆心距离等于半径、对称向量模长相等)等.
总之这题就是考察计算几何基础.
我考试的时候想到的极端情况似乎并没有在数据里出现……题解里也没有说……所以说,有些极端情况,不要过于害怕……
这种玄学计算几何在考试的时候还是不要耗太多精力,天知道有什么鬼事发生,这次考试吓得我给这题估0分……

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
const int N=55;
const double oo=1e15;
#define yy(a,b) (std::fabs((a)-(b))<1e-9)
int n;
struct Poi{double x,y,z;inline void read(){scanf("%lf%lf%lf",&x,&y,&z);}
};
inline double disf(Poi a,Poi b){return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z);
}
inline double dis(Poi a,Poi b){return std::sqrt(disf(a,b));
}
struct Line{Poi s,t;inline void read(){s.read(),t.read();}
}me;
inline double dis(Line a,Poi b){double x=dis(a.s,b);double y=dis(a.t,b);double z=dis(a.s,a.t);if(yy(x+y,z)||yy(x+z,y)||yy(y+z,x))return 0;double cos_k=(x*x+y*y-z*z)/(2*x*y);double sin_k=std::sqrt(1-cos_k*cos_k);double s=x*y*sin_k;return s/z;
}
struct Cir{Poi h;double r;inline void read(){h.read(),scanf("%lf",&r);}
}cir[N];
inline double dis(Cir a,Line b){double dis1=dis(b,a.h);if(dis1>a.r)return oo;double dis2=std::sqrt(a.r*a.r-dis1*dis1);double dis3=std::sqrt(disf(a.h,b.s)-dis1*dis1);double dis4=std::sqrt(disf(a.h,b.t)-dis1*dis1);double dis5=dis(b.s,b.t);if(yy(dis3+dis4,dis5))return dis3-dis2;if(dis3<dis4)return oo;return dis3-dis2;
}
inline Poi go(Line a,double b){double dis1=dis(a.s,a.t);double k=b/dis1;Poi ret;ret.x=(a.t.x-a.s.x)*k+a.s.x;ret.y=(a.t.y-a.s.y)*k+a.s.y;ret.z=(a.t.z-a.s.z)*k+a.s.z;return ret;
}
inline Poi dc(Poi a,Poi b){Poi ret;ret.x=2*b.x-a.x;ret.y=2*b.y-a.y;ret.z=2*b.z-a.z;return ret;
}
inline Poi dc(Poi a,Line b){double dis1=dis(b,a);double dis2=dis(a,b.s);double dis3=dis(a,b.t);double dis4=std::sqrt(dis2*dis2-dis1*dis1);double dis5=std::sqrt(dis3*dis3-dis1*dis1);double dis6=dis(b.s,b.t);Poi mid=go(b,dis4*(yy(dis4+dis5,dis6)||dis3<dis2?1:-1));return dc(a,mid);
}
inline Poi dc(Poi a,Poi b,Poi c){return dc(a,(Line){b,c});
}
inline Line Pia(Line a,Cir b){Poi aha=go(a,dis(b,a));Poi ehe=dc(a.s,aha,b.h);return (Line){aha,ehe};
}
inline void Init(){scanf("%d",&n);int i;for(i=1;i<=n;++i)cir[i].read();me.read();
}
inline int Pang(int last){int i,id=-1;double min=oo,tmp;for(i=1;i<=n;++i){if(i==last)continue;tmp=dis(cir[i],me);if(tmp<min){min=tmp;id=i;}}return id;
}
inline void Work(){int i,next=0;for(i=1;i<=11;++i){next=Pang(next);if(next==-1)return;if(i==11){printf("etc.");return;}printf("%d ",next);me=Pia(me,cir[next]);}
}
int main(){//freopen("rio.txt","r",stdin);
    Init(),Work();return 0;
}

解析几何法、80分

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pf(a) ((a)*(a))
const int N=55;
const double oo=1e15;
int n;
struct Poi{double x,y,z;inline friend Poi operator -(Poi a,Poi b){return (Poi){a.x-b.x,a.y-b.y,a.z-b.z};}inline void read(){scanf("%lf%lf%lf",&x,&y,&z);}
};
struct Line{Poi s,t;inline void read(){s.read(),t.read();t=t-s;}
}me;
struct Cir{Poi h;double r;inline void read(){h.read(),scanf("%lf",&r);}
}cir[N];
inline double dis(Cir a,Line b){double x=b.s.x-a.h.x;double y=b.s.y-a.h.y;double z=b.s.z-a.h.z;double A=pf(b.t.x)+pf(b.t.y)+pf(b.t.z);double B=2*b.t.x*x+2*b.t.y*y+2*b.t.z*z;double C=x*x+y*y+z*z-a.r*a.r;double det=B*B-4*A*C;if(det<0)return oo;double k=(-B-std::sqrt(det))/(2*A);if(k<0)return oo;return std::sqrt(pf(k*b.t.x)+pf(k*b.t.y)+pf(k*b.t.z));
}
inline Poi go(Line a,double b){double k=b/std::sqrt(pf(a.t.x)+pf(a.t.y)+pf(a.t.z));Poi ret;ret.x=a.t.x*k+a.s.x;ret.y=a.t.y*k+a.s.y;ret.z=a.t.z*k+a.s.z;return ret;
}
inline Line Pia(Line a,Cir b){Poi aha=go(a,dis(b,a));Poi o=a.s-aha;Poi m=aha-b.h;Poi t;double k=(2*m.x*o.x+2*m.y*o.y+2*m.z*o.z)/(m.x*m.x+m.y*m.y+m.z*m.z);t.x=k*m.x-o.x;t.y=k*m.y-o.y;t.z=k*m.z-o.z;return (Line){aha,t};
}
inline void Init(){scanf("%d",&n);int i;for(i=1;i<=n;++i)cir[i].read();me.read();
}
inline int Pang(int last){int i,id=-1;double min=oo,tmp;for(i=1;i<=n;++i){if(i==last)continue;tmp=dis(cir[i],me);if(tmp<min){min=tmp;id=i;}}return id;
}
inline void Work(){int i,next=0;for(i=1;i<=11;++i){next=Pang(next);if(next==-1)return;if(i==11){printf("etc.");return;}printf("%d ",next);me=Pia(me,cir[next]);}
}
int main(){Init(),Work();return 0;
}

向量法、100分

T3,考试的时候给这题时间少了,还有就是我的数学能力不强.
我们先看怎么是最大的.那就是n!分解质因数之后,把奇数size的质因数size-1后就是了.为什么,因为每个质因数的size同时达到了最大.
如果按照分解质因数的思路走下去的话,应该会想到的吧?(an,wei,还不是不会观察性质)
那么我们的方案数就是,把那些奇数size质因数分堆成数的方案数*2(*2是因为1选不选都可以).
那么我们经过小程序发现(小程序大用处,小程序意识++,而且判断解题出发点也是比较重要的),这些数很少啊,所以能dfs出许多数来,然而只能得40分,看来这数据造的很良(xin)心(ji)啊.
题解的40分做法,是状压dp啊,不过这玩意也就是用来过渡到100分的(不过分集合,就已经与状压有一定联系了).
100分的话,就是40分状压加按根号分类处理.
根号分类处理似乎在质因数这里用得比较多,而且还经常和状压dp混在一起.当然这玩意还有其他的用处,比如PA某题……

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
typedef long long LL;
const int N=3000;
int prime[N+10],min[N+10],len,size[N+10];
bool isnot[N+10];
int n,lit[N+10],big[N+10],lm,bm,now,last,could[(1<<13)+10];
LL ans,f[2][(1<<13)+10];
inline void Iput(int x){while(min[x])++size[min[x]],x/=prime[min[x]];
}
inline void Init(){isnot[1]=true;min[1]=0;register int i,j;for(i=2;i<=N;++i){if(!isnot[i])prime[++len]=i,min[i]=len;for(j=1;prime[j]*i<=N;++j){isnot[prime[j]*i]=true;min[prime[j]*i]=j;if(i%prime[j]==0)break;}}
}
inline void dp1(){now=0,last=1;f[0][0]=1;int i,j,k,full,sum;full=(1<<lm)-1;for(i=0;i<=full;++i){sum=1;for(j=0;j<lm;++j)if(i&(1<<j)){sum*=lit[j+1];if(sum>n){sum=-1;break;}}could[i]=sum;}for(i=1;i<=lm;++i){full=(1<<(i-1))-1;now^=1,last^=1;memcpy(f[now],f[last],sizeof(f[now]));for(j=0;j<=full;++j){if(could[j|(1<<i-1)]!=-1)for(k=full^j;;k=(k-1)&(full^j)){f[now][(j|(1<<i-1))|k]+=f[last][k];if(!k)break;}}}
}
inline void dp2(){int i,j,k,full=(1<<lm)-1;for(i=1;i<=bm;++i){now^=1,last^=1;memset(f[now],0,sizeof(f[now]));for(j=0;j<=full;++j){if(could[j]!=-1&&could[j]*big[i]<=n)for(k=full^j;;k=(k-1)&(full^j)){f[now][j|k]+=f[last][k];    if(!k)break;}}}ans=f[now][full];
}
inline void Work(){scanf("%d",&n);int i,st=std::sqrt(n+0.5);for(i=1;i<=n;++i)Iput(i);for(i=1;i<=len;++i){if((size[i]&1)==0||size[i]==1)continue;if(prime[i]<=st)lit[++lm]=prime[i];else big[++bm]=prime[i];}dp1(),dp2();printf("%lld\n",ans<<1);
}
int main(){Init(),Work();return 0;
}

Kod


No.22省选模拟赛
相对来说,这次的题还是比较简单吧.
本来是先看一遍题,不过这次一边看一边打了.
T1,题干简洁,应该是个反演,先把暴力码出来.码完去看T2,题干简洁,不是很会,先码出来一个12分暴力.然后去看T3,感觉从哪里见过,不管了,先码个暴力,不对啊,我的复杂度不对,又想了想,想出来一个小性质,然后开始码,码着码着突然意识到我tm打的是正解啊.打完T3,没调,又回到了T1,推了一下,推出来一个O(n^2ln(n^2))的做法,码了出来,测了一下极限数据,4s,完蛋.我先拿这玩意和暴力拍了一下,然后卡了卡常,卡到了2.1s,静态查了一波错,一看时间不对,感觉在这里查错时间太长了,而且太低效了.于是去调T3,我干,这玩意咋打暴力啊,造数据麻烦也就算了,tmd不会打暴力啊,YY了一会,不会,于是赶紧静态查错一波,去看T2了.T2,在原来12分的dfs基础之上,打了一个32分的状压.考试时间不多了,于是去手造小样例测试T3程序.
最后100+32+100=232分……
T1,我的做法正好卡过……正解是O(n^2lnn)的,写丑了和我的做法差不多……不过着这玩意出5000还真是有点卡……
考试查错的时候,有点困,所以查错过于慢,效率也过于低了.
我的式子和他的式子基本一样,只不过写法不一样,所以导致,解法不一样.
O(n^2)预处理gcd用得还挺多,而且对于特殊模数利用自然溢出是可以卡常的.
一维一维得推广,不只是在递推和组合数学的时候用到,反演也是可以的.
T2,我只是想到了压每一位,但是没想到去换个角度搞,但是我感觉我当时确实有点不冷静,也有点困,还有点“踌躇满志”……
所以说以上说的这些,不能每次光说不改,马上就要省选了……
这道题的思路具体就是解决不合法方案,正解的方法是类似容斥的方法,看到之后,发现十分清真美丽.
还有一种方法,可以“强制合法”.zzh就是这种方法.这种方法稳72,但是对于这道题的数据可以卡到92.思路就是统计同一类状态的可能结束点数,这样就可以计算了.
这题的dp还是比较妙的啊.
T3,是一道十分好想且好写的半平面交,只不过到现在我还是很不会写暴力.
所以说不会写暴力的话,就赶快去手玩小样例吧.
zzh似乎被50分暴力套住,以为自己想到的是50分暴力,所以就把100分打成了50分.一开始我也以为自己的正解是50分暴力,后来打着打着才发现自己是正解,所以分析复杂度还是很重要的吧.


(这个是最后一篇,当时还没写完……写的时候在进行高密度考试所以和之前写得不一样……)

现在,我们的“肚子”是无限大的,然而“吃饭”是需要时间的.所以说,既然现在“食物”多了,那我们挑着“好吃的”去“吃”.
思考与总结,写下来是次要的,进脑子才重要.
avoid:
  困倦、慌乱、自满、犹豫不决、兴奋;
  考试之外的事情带到考试上来;
  为不必要的小事去找不值得的气;
  盲目去冒一些不必要的险,比如在不确定的情况下使用不熟悉的知识点;
  同样的错误重复出现;
exp:
  路子不通,换路子;
  不要放弃任何一道题,再看两眼就会有新的希望;
  犹豫不决会把小的问题变大,会把小的疑惑放大,所以说遇事果决是不错的选择;
  保持镇定,时刻冷静,是拿高分的基础;
  想要翻盘,就不要想着自己在翻盘,要想着自己正在打一场新的比赛(这句话不只是说一试,同样也在说在一场考试中的前后期);
  别人的节奏是别人的节奏,自己的路子是自己的路子,就算别人AK,跟我没关系,就算别人爆零,跟我也没关系;
  多一些思考,多一线希望;
  时间分配的均衡度与得分往往是正相关的;
  码,要思路清晰,速战速决;
  调,要保持冷静,速战速决;
  想打对拍就不要拖延;
  想验证程序,但是不会写暴力,或者不会造数据,就快手造小样例,并且加大眼动(脑动)查错力度;
  没时间优化算法,可以,但是懒得去优化是绝对不可以的,尤其是你明知道你的程序慢,还不去优化;
  不要觉得正解的时间复杂度一定不卡时限;
  正确分析复杂度有时候会让你避免不必要的错误;
knowledge:
  剪枝是dfs的重中之重;
  最小割的Inf边,是网络流的巧妙;
  No.22省选模拟赛:
    O(n^2)预处理gcd可以用来减少复杂度;
    维数递增地推广,不仅仅是递推可以用,一些数学规律,数学公式也可以尝试这样做;
    遇到特殊模数,有时候可以用自然溢出卡常;
    分析问题,转化问题,会方便你设计solution;
    类似差分又类似容斥地减去无用状态,会方便dp;
    积累反演式子:

待续……

转载于:https://www.cnblogs.com/TSHugh/p/8543095.html

省选模拟赛记录(越往下越新哦~~~)相关推荐

  1. 省选模拟赛2022/3/23

    省选模拟赛2022/3/23 比赛时间安排 赛后总结反思 与正解的差距 T1 T2 T3 比赛时间安排 7.30-7.40 t1 n<=10,全排列一下跳叶子结点的顺序暴力搞 t2 题意感觉好复 ...

  2. 省选模拟赛2022/3/31

    省选模拟赛2022/3/31 比赛时间安排 赛后反思总结 与正解的差距 T3 比赛时间安排 7.50-8.10 t1 暴力复杂度都很大啊,完全没想法 t2 dfs可以试一试,但是感觉复杂度还是会炸,想 ...

  3. 省选模拟赛(正睿的最后一场)

    省选模拟赛(正睿的最后一场!!) 比赛时间安排 与正解的差距 T1 比赛时间安排 7.30-7.40 t1 能拿10分dfs,n=0的情况或许可以找规律 t2 看不懂 t3 暴力挺好写的,求lca太麻 ...

  4. NOI2019省选模拟赛 第五场

    爆炸了QAQ 传送门 \(A\) \(Mas\)的童年 这题我怎么感觉好像做过--我记得那个时候还因为没有取\(min\)结果\(100\to 0\)-- 因为是个异或我们肯定得按位考虑贡献了 把\( ...

  5. test 7 3-22 2021省选模拟赛seven

    文章目录 考试复盘 人生 赢家 黑红兔 考试复盘 T1T1T1 subtask1:n≤5subtask1:n\le 5subtask1:n≤5,暴搜点的颜色状态以及边的存在状态 对于一条连接相同颜色点 ...

  6. 【总结】NOIP模拟赛记录

    时间栏为粗略时间,并不精准. 10.15三校联考(100+60+0) 操作内容 时间 备注 通看所有题意 10min T1正解+调试 30min T1暴力+数据生成器,并开始对拍 10min T2思考 ...

  7. 20200515省选模拟赛B、幻化成风(毒瘤容斥题+构造容斥系数+生成函数+hash状压DP+Trie树优化背包)

    题解 花了一上午+一中午终于把这道题A了 首先,我们要求的是bi互不相同的合法方案数 我们可以枚举一个a的集合S,来强制里面的b全部都相同,然后其它的随便放 由于这个题的n的约数非常多,我们可以把它质 ...

  8. snoi 省选模拟赛day2t1 bzoj 2873: 光之大陆

    Description 在光之大陆的土地上,各种势力盘根错节.来自光之峡谷的精灵,来自黑暗森林的亡灵,来自古老东方的人类共同生活在一起.善于打造装置的矮人,善于发明的侏儒,隐匿于山林的巨人也坚守着属于 ...

  9. test6 3-21 2021省选模拟赛six

    文章目录 考试复盘 rng lg pm 考试复盘 第一题,乍一看期望,又不会做了,乍二看,暴力好像可以202020跑路,屁颠屁颠敲完死活过不了这个简单的样例:开始(⊙⊙?)乍三看,实数??完了直接哦豁 ...

最新文章

  1. element 使用阿里图标变形了_web前端大厂面试题(阿里云笔试篇)
  2. 汇编: mul乘法指令
  3. Origin如何绘制泡泡图?
  4. Vue组件及自定义事件
  5. python设计一个动物类_我用Python实现了12500张猫狗图像的精准分类
  6. C语言文件与数组之间输入输出操作
  7. windows7+tomcat7+nginx1.11.3 +memcached
  8. leetcode 144. Binary Tree Preorder Traversal ----- java
  9. sqlserver的存储过程
  10. mysql 种子表_mysql之3种子查询
  11. SVN:下载、安装和中文设置
  12. 编写优质嵌入式C程序
  13. n分频器 verilog_verilog 语言实现任意分频
  14. 阴阳师服务器维护3月25日,阴阳师3月25日更新内容-阴阳师3月25日更新内容介绍_牛游戏网...
  15. 计算机专业浙大对比中科大,浙大,东南大学,中科大通信专业比较!!!!!!!!...
  16. 年轻人哪,不能太舒服了
  17. python英文单词整理
  18. PowerShell : 如何设置输出颜色,Format-Color让黑乎乎的窗口丰富起来
  19. matlab特征值归一化,如何用matlab将特征向量归一化呢?
  20. openoffice linux 目录,Linux下openoffice的安装

热门文章

  1. canvas 圆角矩形填充_View绘制系列(9)Canvas八卦图绘制
  2. 灵动标签 php脚本,帝国CMS灵动标签PHP代码实现标签无限嵌套的效果
  3. java安卓的xml布局,android – 将xml布局更改为java代码
  4. 太极计算机长沙,他设计了湖南最有尊严的建筑,诗意得让你想在里面加班
  5. dingo php,详细介绍Laravel+Dingo/Api 自定义响应
  6. 新疆计算机一级考试试题手机软件,新疆维吾尔自治区计算机一级考试理论题库(最新最完整)...
  7. Codeforces 1323 div2题解ABC
  8. html两个框架同时_两个框架的故事
  9. sql 12天内的数据_想要在12周内成为数据科学家吗?
  10. 信用卡还不起会有什么严重后果?