本次月赛出了6题。Rank 2。
1002果断TLE。然后再用拓展欧几里得算法,再次WA到死。
然后最后rejudge了。
回文数的正解居然是要排序的,我写的是按顺序弄出来的。。。一路狂WA。。。
最后就这样悲剧了。
悲剧+沙茶。。。
没啥说的。。。BS我这沙茶吧
1001:http://hi.baidu.com/edwardmj/blog/item/7973c631706b6af41a4cff36.html
1002:http://hi.baidu.com/edwardmj/blog/item/432eec95b9c7bc6155fb9628.html
1003:http://hi.baidu.com/edwardmj/blog/item/bdd0b333ad4f3215ebc4af59.html
1004:http://hi.baidu.com/edwardmj/blog/item/b40d62d19d4bc038970a1612.html
1005:http://hi.baidu.com/edwardmj/blog/item/82fcbcc49ecc4f179d163d21.html
1006:http://hi.baidu.com/edwardmj/blog/item/46f4fcd30c4a34349b5027e2.html
1007:http://hi.baidu.com/edwardmj/blog/item/ddefd41529d7340d4a90a71c.html
另外1003在SCOI测试中,匹配是比并查集快的,但是这里匹配会TLE,只能用并差集。
[SPOJ2798 QTREE3]树链剖分、线段树
【题目地址】https://www.spoj.pl/problems/QTREE3/
【题目大意】
给定一棵树,一开始所有点都是白色的。
给定两个操作:
0 x:把点x变色(本来是白色变成黑色,本来是黑色变成白色)
1 x:求从结点1到点x的路径上,第一个黑色的点是哪一个?如果没有,输出-1。
【算法分析】
树链剖分例题(比本文详细):http://hi.baidu.com/edwardmj/blog/item/2894235220a1be501038c2c9.html
首先显然把结点1当做根变成有根树。
轻重边剖分,然后都于每个重路径建立一棵线段树,然后对于操作0,就把线段树里对应那个点取反即可。
然后对于操作1,就从点x一直向父亲走,遇到重路径就跳到重路径的起始端,并用线段树取最小值,遇到轻边的话就直接走。
【其它】
跑了12s+。。。。果然我写的程序总是比较慢= =
【CODE】
#include
const int INF=1000000000;
struct gtp{int x,y,next,op,inlist;}g[N*2];
struct TT{int l,r,zz;}tr[N*8];
int n,m,e,times,tot,ct,ans;
int ls[N],list[N],fa[N],Size[N],myheavy[N],black[N];
int color[N],cst[N];
void addedge(int x,int y){
g[++e].x=x; g[e].y=y; g[e].next=ls[x]; ls[x]=e; g[e].op=e+1;
g[++e].x=y; g[e].y=x; g[e].next=ls[y]; ls[y]=e; g[e].op=e-1;
}
void input(){
scanf("%d%d",&n,&m);
for (int i=1,x,y;i
addedge(x,y);
}
}
void predfs(int p){
Size[p]=times++;
for (int t=ls[p];t;t=g[t].next)
if (!fa[p] || t!=g[fa[p]].op){
fa[g[t].y]=t;
predfs(g[t].y);
}
Size[p]=times-Size[p];
}
void dfs(int p){
int mt,mm=-INF;
for (int t=ls[p];t;t=g[t].next)
if ((!fa[p] || t!=g[fa[p]].op) && Size[g[t].y]>mm){
mm=Size[g[t].y];
mt=t;
}
if (mm==-INF) return;
myheavy[p]=mt;
list[++tot]=mt;
g[mt].inlist=tot;
dfs(g[mt].y);
for (int t=ls[p];t;t=g[t].next)
if ((!fa[p] || t!=g[fa[p]].op) && t!=mt)
dfs(g[t].y);
}
void draw_color(){
ct=color[1]=cst[1]=1;
for (int i=2;i<=tot;i++){
if (g[list[i-1]].y!=g[list[i]].x) cst[++ct]=i;
color[i]=ct;
}
}
void build(int p,int l,int r){
tr[p].l=l; tr[p].r=r; tr[p].zz=0;
if (l==r) return;
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void change(int p,int x){
if (tr[p].l==tr[p].r){
if (tr[p].zz) tr[p].zz=0;
else tr[p].zz=g[list[x]].x;
return;
}
int mid=(tr[p].l+tr[p].r)/2;
if (x<=mid) change(p*2,x);
else change(p*2+1,x);
if (tr[p*2].zz) tr[p].zz=tr[p*2].zz;
else tr[p].zz=tr[p*2+1].zz;
}
void Get_zz(int p,int l,int r){
if (l<=tr[p].l && tr[p].r<=r){
if (tr[p].zz) ans=tr[p].zz;
return;
}
int mid=(tr[p].l+tr[p].r)/2;
if (r>mid) Get_zz(p*2+1,l,r);
if (l<=mid) Get_zz(p*2,l,r);
}
void Try(int x){
if (black[x]) ans=x;
if (x==1) return;
if (!g[fa[x]].inlist) Try(g[fa[x]].x);
else{
Get_zz(1,cst[color[g[fa[x]].inlist]],g[fa[x]].inlist);
Try(g[list[cst[color[g[fa[x]].inlist]]]].x);
}
}
void deal(){
for (int op,x,i=1;i<=m;i++){
scanf("%d%d",&op,&x);
if (op==0){
black[x]^=1;
if (myheavy[x]) change(1,g[myheavy[x]].inlist);
}else{
ans=0;
Try(x);
if (ans) printf("%dn",ans);
else printf("-1n");
}
}
}
int main(){
input();
predfs(1);
dfs(1);
draw_color();
build(1,1,tot);
deal();
}
[SPOJ913 QTREE2]LCA、DFS
【题目大意】
给出一棵带权的树。有两种询问。
1、问节点a到结点b路径的权和。
2、问节点a到结点b路径中第k个点是哪个。
【算法分析】
直接搞个RMQ来算LCA。
对于询问1,轻松得出ans=dist[a]+dist[b]-dist[LCA(a,b)]
对于询问2,按照a和b到LCA(a,b)的距离,可以判断他在LCA到a的链上还是到b的链上。
进一步转化为询问(x,y):x上根走y步到达哪一个节点。
我们先把询问都放进链表里,然后一次记录路径的DFS,轻松解决。
【其他】1A
【CODE】
#include
const int QQ=1000000;
struct edge{int y,w;edge *next;}space[N*2],*ls[N],Qspace[QQ],*Qls[N];
int n,e,Tc,Qe,tot,Qn;
int dist[N],dep[N],list[N*2],R[N],ans[QQ];
int rmq_node[17][N*2],rmq_dep[17][N*2],lg[N*2];
void addedge(int x,int y,int w){
space[++e].y=y; space[e].w=w;
space[e].next=ls[x]; ls[x]=&space[e];
}
void predfs(int p,int fa){
list[++tot]=p;
R[p]=tot;
for (edge *t=ls[p];t;t=t->next)
if (t->y!=fa){
dist[t->y]=dist[p]+t->w;
dep[t->y]=dep[p]+1;
predfs(t->y,p);
list[++tot]=p;
}
}
void buildrmq(){
for (int i=1;i<=tot;i++){
rmq_node[0][i]=list[i];
rmq_dep[0][i]=dep[list[i]];
}
for (int k=1;1<
rmq_node[k][i]=rmq_node[k-1][i];
}else{
rmq_dep[k][i]=rmq_dep[k-1][i+(1<
}
void init(){
e=0; memset(ls,0,sizeof(ls));
Qn=0; Qe=0; memset(Qls,0,sizeof(Qls));
scanf("%d",&n);
for (int i=1,x,y,w;i
addedge(x,y,w);
addedge(y,x,w);
}
dist[1]=0; dep[1]=0; tot=0;
predfs(1,0);
buildrmq();
}
void addquestion(int x,int y){
Qspace[++Qe].y=y; Qspace[Qe].next=Qls[x]; Qls[x]=&Qspace[Qe];
Qspace[Qe].w=Qn;
}
int LCA(int l,int r){
if (l>r){
int tmp=l; l=r; r=tmp;
}
int k=lg[r-l+1];
if (rmq_dep[k][l]
else
return rmq_node[k][r-(1<
void build_question(){
char op[100];
int x,y,k,p;
for (Qn=1;;Qn++){
scanf("%s",op+1);
if (op[1]==’D’ && op[2]==’O’){
Qn–;
break;
}
if (op[1]==’D’){
scanf("%d%d",&x,&y);
p=LCA(R[x],R[y]);
ans[Qn]=dist[x]-2*dist[p]+dist[y];
}
else{
scanf("%d%d%d",&x,&y,&k);
p=LCA(R[x],R[y]);
if (dep[x]-dep[p]+1==k) ans[Qn]=p;
else if (k
}
}
}
void solve(int p,int fa){
list[++tot]=p;
for (edge *q=Qls[p];q;q=q->next)
ans[q->w]=list[tot-q->y];
for (edge *t=ls[p];t;t=t->next)
if (t->y!=fa) solve(t->y,p);
tot–;
}
int main(){
lg[1]=0;
for (int i=2;i
if (1<
scanf("%d",&Tc);
for (int i=1;i<=Tc;i++){
init();
build_question();
tot=0;
solve(1,0);
for (int j=1;j<=Qn;j++) printf("%dn",ans[j]);
}
}
[FZU 1833 Counting Problem]数论、组合数、扩展欧几里得算法解模线性方程
【题目地址】http://acm.fzu.edu.cn/problem.php?pid=1833
【题目大意】
求P(sum(Ai),n*n) / (A1! * A2! * A3! …… * Ak!)
根据出题人福大核武的指示,本题考的是数论,并不是组合数学,而由于这个公式已经水得连我都能推出来,就直接写算了。。。
【算法分析】
首先做之前应该清楚几点:
1、一个数n的素因子是O(lg n)级别的。
2、没有任何辅助工具(素数表之类)的时候,将一个数分解质因数的复杂度是O(sqrt(n))级别的。
3、AC大神的 n! 质因数分解模板的复杂度是
做法:
1、对M分解质因数
2、对公式的分母中红字部分分解质因数,并且分开自己拥有的质因数和M所拥有的质因数。
3、由于Sum<=100*5000,所以可以暴力求P(sum,n*n)。就是直接从n*n乘到n*n-sum+1。
乘的过程中把M所含的质因子也分离开来,和上面的那个蓝色的进行幂减。幂减以后,把各部分再分别合起来,弄成分子s1和分母s2,我们就可以惊讶地发现。。。gcd(s2,M)=1。
4、由于gcd(s2,M)=1,那么对应的 s1 = s2*x (mod M) 的解就变成了唯一的。(这个不知道对不对=_=,不会证明)
5、对于那个模线性方程,变形得 s1= s2*x + M*y。这个用欧几里得拓展算法搞定。
【其他】
Orz AC大神~
据其指示,时间和第一名居然是一样的~
1WA、1TLE。。。
WA:
最后一步搞了一会儿,一开始以为直接 s1 * s2^(M-2) % M,但是原来M必须为质数才能搞。
上面的第4步得到的gcd(s2,M)=1并不能让这个公式成立。。。
TLE:
我脑残地不听AC大神的劝告,直接枚举答案求逆元去了。。。
再Orz 一下我的各种脑残。。。果然数论我已经水到一种境界了。。。
【CODE】
#include
typedef long long lld;
int n,mod,cn,mtot,tot2;
int a[105];
int pl[50],cl[50],pl2[5005],cl2[5005];
bool ss[5005],hash[5005];
lld s1,s2;
void init(){
int i,j;
tot2=0;
memset(ss,true,sizeof(ss));
for (i=2;i*i<=5000;i++)
for (j=2;i*j<=5000;j++)
ss[i*j]=false;
for (i=2;i<=5000;i++)
if (!hash[i] && ss[i])
pl2[++tot2]=i;
}
void dealm(){
int i,j,x=mod;
bool flag;
for (i=2;i*i<=mod;i++){
flag=true;
while (x%i==0){
if (i<=5000) hash[i]=true;
if (flag) pl[++mtot]=i;
x/=i;
flag=false;
}
}
if (x!=1){
pl[++mtot]=x;
if (x<=5000) hash[x]=true;
}
}
void Div(int *list,int n){
if (!n) return;
int i,tmp;
for (i=1;i<=mtot && pl[i]<=n;i++){
tmp=n;
while (tmp/pl[i]){
list[i]-=tmp/pl[i];
tmp/=pl[i];
}
}
}
void Div2(int *list,int n){
if (!n) return;
int i,tmp;
for (i=1;i<=tot2 && pl2[i]<=n;i++){
tmp=n;
while (tmp/pl2[i]){
list[i]+=tmp/pl2[i];
tmp/=pl2[i];
}
}
}
void Fen(int x){
if (!x) return;
int i;
for (i=1;i<=mtot && x>=pl[i];i++)
while (x%pl[i]==0){
cl[i]++;
x/=pl[i];
}
s1=s1*x%mod;
}
lld pow_mod(lld x,lld cf){
if (cf==0) return 1;
lld res=pow_mod(x,cf/2);
res=res*res%mod;
if (cf&1) res=res*x%mod;
return res;
}
lld exgcd(lld a,lld b,lld &x,lld &y){
if (b==0){
x=1; y=0;
return a;
}
lld res=exgcd(b,a%b,x,y),tx=x,ty=y;
x=ty; y=tx-a/b*ty;
return res;
}
void solve(){
lld x,y,rate=s1/exgcd(s2,mod,x,y);
x=x%mod*rate%mod;
x=(x+mod)%mod;
cout << x << endl;
}
int main(){
while (scanf("%d%d%d",&n,&mod,&cn)!=EOF){
mtot=0; tot2=0;
s1=1; s2=1;
memset(cl,0,sizeof(cl));
memset(hash,false,sizeof(hash));
memset(cl2,0,sizeof(cl2));
int sum=0;
for (int i=1;i<=cn;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
if (mod==1){
cout << 0 << endl;
continue;
}
dealm();
init();
for (int i=1;i<=cn;i++){
Div(cl,a[i]);
Div2(cl2,a[i]);
}
for (int i=n*n;i>n*n-sum;i–) Fen(i);
for (int i=1;i<=mtot;i++)
s1=s1*pow_mod(pl[i],cl[i])%mod;
for (int i=1;i<=tot2;i++)
s2=s2*pow_mod(pl2[i],cl2[i])%mod;
solve();
}
}
[SCOI 第二试 传送带]二分法 or 二分+暴力 or 直接暴力
【题目】
Description
在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段。两条传送带分别为线段AB和线段CD。lxhgww在AB上的移动速度为P,在CD上的 移动速度为Q,在平面上的移动速度R。现在lxhgww想从A点走到D点,他想知道最少需要走多长时间
Input
输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By
第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,Dx,Dy
第三行是3个整数,分别是P,Q,R
Output
输出数据为一行,表示lxhgww从A点走到D点的最短时间,保留到小数点后2位
Sample Input
0 0 0 100
100 0 100 100
2 2 1
Sample Output
136.60
Hint
对于100%的数据,1<= Ax,Ay,Bx,By,Cx,Cy,Dx,Dy<=1000
1<=P,Q,R<=10
【算法分析】
好吧,很容易得出路径应该是这样一种形式:
A->p1->p2->D
其中p1在线段AB上,p2在线段CD上。
然后感觉应该是都满足单峰性质的。
表示不会证明。。。只是感觉。
可以用r1 * AB向量和r2 * DC向量分别表示p1、p2。
第一种:
嵌套二分r1和r2。得到速度非常快的一个算法。
第二种:
如果不放心,那么枚举r1,精度卡一下,卡1e-5之类。
然后二分r2。
得到一个比较快的算法。
第三种:
如果还不放心,那么直接暴力它吧。经测试,直接枚举r1和r2,卡精度2e-4,可以在每个测试数据1.8s的情况下暴力AC!
果然,很黄很暴力~
【其他】
我是傻X,因为求的是最小值,而不是最大值。
一开始二分的部分应该l=mid的时候写成r=mid
应该r=mid的时候写成了l=mid。
导致以为不符合单峰性质,所以有了各种暴力尝试。。。
【CODE1】
#include
const double eps=1e-6;
int Ax,Ay,Bx,By,Cx,Cy,Dx,Dy,Rab,Rcd,R;
double xx1,yy1,xx2,yy2,d1,d2,d3,r1;
inline double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
inline double Get(double r2){
xx1=(Bx-Ax)*r1+Ax;
yy1=(By-Ay)*r1+Ay;
xx2=(Cx-Dx)*r2+Dx;
yy2=(Cy-Dy)*r2+Dy;
d1=dis(xx1,yy1,Ax,Ay)/Rab;
d2=dis(xx1,yy1,xx2,yy2)/R;
d3=dis(xx2,yy2,Dx,Dy)/Rcd;
return d1+d2+d3;
}
double solve(double tmp){
r1=tmp;
double l=0,r=1,mid;
while (l+eps
if (Get(mid)
}
return Get(l);
}
int main(){
cin>>Ax>>Ay>>Bx>>By>>Cx>>Cy>>Dx>>Dy>>Rab>>Rcd>>R;
double l=0,r=1,mid;
while (l+eps
if (solve(mid)
}
printf("%.2lfn",solve(l));
}
【CODE2】
#include
const double eps=1e-5;
int Ax,Ay,Bx,By,Cx,Cy,Dx,Dy,Rab,Rcd,R;
double xx1,yy1,xx2,yy2,d1,d2,d3,r1;
inline double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
inline double Get(double r2){
xx1=(Bx-Ax)*r1+Ax;
yy1=(By-Ay)*r1+Ay;
xx2=(Cx-Dx)*r2+Dx;
yy2=(Cy-Dy)*r2+Dy;
d1=dis(xx1,yy1,Ax,Ay)/Rab;
d2=dis(xx1,yy1,xx2,yy2)/R;
d3=dis(xx2,yy2,Dx,Dy)/Rcd;
return d1+d2+d3;
}
double solve(double tmp){
r1=tmp;
double l=0,r=1,mid;
while (l+eps
if (Get(mid)
}
return Get(l);
}
int main(){
cin>>Ax>>Ay>>Bx>>By>>Cx>>Cy>>Dx>>Dy>>Rab>>Rcd>>R;
double i=0,ans=1e20;
for (i=0;i<1;i+=eps)
ans=min(ans,solve(i));
printf("%.2lfn",ans);
}
【CODE3】
#include
const double eps=1e-6;
const double eps2=1e-7;
const double eeps=2e-4;
int Ax,Ay,Bx,By,Cx,Cy,Dx,Dy,Rab,Rcd,R;
inline double dis(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
inline double Get(double r1,double r2){
double x1,y1,x2,y2,d1,d2,d3;
x1=(Bx-Ax)*r1+Ax;
y1=(By-Ay)*r1+Ay;
x2=(Cx-Dx)*r2+Dx;
y2=(Cy-Dy)*r2+Dy;
d1=dis(x1,y1,Ax,Ay)/Rab;
d2=dis(x1,y1,x2,y2)/R;
d3=dis(x2,y2,Dx,Dy)/Rcd;
return d1+d2+d3;
}
double solve(double r1){
double l,ans=1e20;
for (l=0;l<1;l+=eeps) ans=min(ans,Get(r1,l));
return ans;
}
int main(){
cin>>Ax>>Ay>>Bx>>By>>Cx>>Cy>>Dx>>Dy>>Rab>>Rcd>>R;
double l=0,r=1,mid,ans=1e20;
for (l=0;l<1;l+=eeps) ans=min(ans,solve(l));
printf("%.2lfn",ans);
}