BZOJ3522: [Poi2014]Hotel
Aug 18, 2015 08:01:14 PM
题解:
考虑三个点在树上的形态:
(1)成一条链(其实就是中心是三个点中的一个点)
(2)存在一个唯一中心连接三个点,且中心到三个点的路径除了中心之外不相交
(定义自己脑补一下吧...)
显然只有在(2)情况下,并且唯一中心到三个点的路径长度相等才能满足条件.
我们直接枚举唯一中心,以其为根做有根树,则三个点必须分布在不同的子树中,且深度相同.
这个就很容易搞了,看代码搞搞就行了.
代码:
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; #define N 5010 int head[N],next[N<<1],end[N<<1]; inline void addedge(int a,int b){ static int q=1; end[q]=b; next[q]=head[a]; head[a]=q++; } inline void make(int a,int b){ addedge(a,b); addedge(b,a); } int sum1[N]; ll sum2[N]; struct Array{ int a[N],t[N],tclock; inline int operator[](int x){ if(t[x]!=tclock){ t[x]=tclock; a[x]=0; } return a[x]; } inline void add(int x){ if(t[x]!=tclock){ t[x]=tclock; a[x]=0; } ++a[x]; } }temp; int max_dep; inline void dfs(int x,int fa,int dep){ temp.add(dep); max_dep=max(max_dep,dep); for(int j=head[x];j;j=next[j]) if(end[j]!=fa) dfs(end[j],x,dep+1); } int main(){ #ifndef ONLINE_JUDGE freopen("tt.in","r",stdin); #endif int n; scanf("%d",&n); int i,j,k,a,b; for(i=1;i<n;++i){ scanf("%d%d",&a,&b); make(a,b); } ll ans=0; int son; for(i=1;i<=n;++i){ memset(sum1,0,sizeof sum1); memset(sum2,0,sizeof sum2); for(son=1,j=head[i];j;j=next[j],++son){ ++temp.tclock; max_dep=0; dfs(end[j],i,1); for(k=1;k<=max_dep;++k){ if(son>=3) ans+=sum2[k]*temp[k]; sum2[k]+=temp[k]*sum1[k]; sum1[k]+=temp[k]; } } } cout<<ans<<endl; return 0; }
BZOJ1704: [Usaco2007 Mar]Face The Right Way 自动转身机
Aug 18, 2015 07:53:41 PM
题解:
首先枚举$k$,那我们需要知道给定$k$,最少进行多少次操作.
显然应该从小到大进行扫描,考虑当前位置$i$,若颜色不对,则反转$[i,i+k-1]$.
然后再判定剩下的位置颜色对不对.
这个过程可以利用单调队列轻松实现,详情见代码.
代码:
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<algorithm> using namespace std; #define N 5010 bool a[N]; int q[N],fr,ta; int main(){ int n; scanf("%d",&n); int i,j; char s[10]; for(i=1;i<=n;++i){ scanf("%s",s); a[i]=(s[0]=='F'); } int ans=0x3f3f3f3f,temp,k; for(i=1;i<=n;++i){ fr=ta=0; for(temp=0,j=1;j+i-1<=n;++j){ while(fr!=ta&&q[fr]+i-1<j) ++fr; if((((ta-fr)&1)^a[j])==0){ q[ta++]=j; ++temp; } } bool ok=1; for(j=n+2-i;j<=n;++j){ while(fr!=ta&&q[fr]+i-1<j) ++fr; if((((ta-fr)&1)^a[j])==0) ok=0; } if(ok){ if(temp<ans){ ans=temp; k=i; } } } cout<<k<<" "<<ans<<endl; return 0; }
BZOJ1819: [JSOI]Word Query电子字典 暴力+Trie树
Mar 06, 2015 03:11:42 PM
思路:
对于每次询问,暴力枚举所有编辑距离为1的串,然后用Trie判断存不存在并判重.
妈呀这是\(O(10000*26*20*20)=10^8\),居然也能过.
其实用Hash感觉就好多了.
有没有更好的方法?目前没想出来.
#include<cstdio> #include<cstring> #include<cctype> #include<climits> #include<iostream> #include<algorithm> using namespace std; int ch[200010][26],end[200010],vis[200010],cnt; char s[21],ss[22]; int tclock; inline bool judge(int len){ int p=0; for(int i=0;i<len;++i){ if(!ch[p][ss[i]-'a'])return 0; p=ch[p][ss[i]-'a']; } if(!end[p])return 0; if(vis[p]!=tclock)return vis[p]=tclock,1;else return 0; } int main(){ int n,m;scanf("%d%d",&n,&m); register int i,j,k; int len,p,y; while(n--){ scanf("%s",s); len=strlen(s),p=0; for(i=0;i<len;++i){ y=s[i]-'a'; if(!ch[p][y])ch[p][y]=++cnt; p=ch[p][y]; } end[p]=1; } int id; while(m--){ scanf("%s",s); len=strlen(s); p=0; bool find=1; for(i=0;i<len;++i){ y=s[i]-'a'; if(!ch[p][y]){find=0;break;} else p=ch[p][y]; } if(find&&end[p]){puts("-1");continue;} int re=0; ++tclock; if(len>1){ for(i=0;i<len;++i){ id=0; for(j=0;j<i;++j)ss[id++]=s[j]; for(j=i+1;j<len;++j)ss[id++]=s[j]; re+=judge(len-1); } } for(i=0;i<len;++i)for(j=0;j<26;++j)if(j!=s[i]-'a'){ memcpy(ss,s,sizeof s),ss[i]='a'+j; re+=judge(len); } for(i=0;i<=len;++i)for(j=0;j<26;++j){ id=0; for(k=0;k<i;++k)ss[id++]=s[k]; ss[id++]='a'+j; for(k=i;k<len;++k)ss[id++]=s[k]; re+=judge(len+1); } printf("%d\n",re); } return 0; }
BZOJ1570:[JSOI2008]Blue Mary的旅行 暴力+网络流
Mar 06, 2015 02:05:50 PM
思路:
暴力枚举答案,然后将点分层,边总是从该层连向下一层.
然后判断最大流是不是满足题意.
具体连边看代码.
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<algorithm> using namespace std; #define INF ~0U>>1 struct Solver{ static const int V=50*100; static const int E=2450*100*2; int head[V],next[E],end[E],flow[E],ind; int d[V],gap[V],stk[V],top,cur[V]; inline void reset(){ ind=top=0,memset(head,-1,sizeof head); } inline void addedge(int a,int b,int f){ int q=ind++;end[q]=b,next[q]=head[a],head[a]=q,flow[q]=f; } inline void make(int a,int b,int f){ /*printf("%d %d %d\n",a,b,f);*/addedge(a,b,f),addedge(b,a,0); } inline void bfs(int T){ static int q[V];int fr=0,ta=0; memset(gap,0,sizeof gap),memset(d,-1,sizeof d),++gap[d[T]=0],q[ta++]=T; while(fr^ta){ int i=q[fr++]; for(int j=head[i];j!=-1;j=next[j])if(d[end[j]]==-1) ++gap[d[end[j]]=d[i]+1],q[ta++]=end[j]; } } inline int Maxflow(int S,int T){ bfs(T),memcpy(cur,head,sizeof cur); int re=0,Min,ins,i,u=S; while(d[S]<T-S+1){ if(u==T){ for(Min=INF,i=0;i<top;++i)if(Min>flow[stk[i]])ins=i,Min=flow[stk[i]]; for(re+=Min,i=0;i<top;++i)flow[stk[i]]-=Min,flow[stk[i]^1]+=Min; u=end[stk[top=ins]^1]; } if(u!=T&&!gap[d[u]-1])break; bool find=0; for(int&j=cur[u];j!=-1;j=next[j])if(flow[j]&&d[end[j]]+1==d[u]){ find=1,ins=j;break; } if(find){stk[top++]=cur[u]=ins,u=end[ins];} else{ Min=T-S+1; for(int j=head[u];j!=-1;j=next[j])if(flow[j]&&d[end[j]]<Min) cur[u]=j,Min=d[end[j]]; if(!--gap[d[u]])break; ++gap[d[u]=Min+1]; if(u!=S)u=end[stk[--top]^1]; } } return re; } }Stg; struct Edge{ int u,v,f; }E[2510]; #define get(x,dep) ((dep-1)*n+x) int main(){ int n,m,T;scanf("%d%d%d",&n,&m,&T); register int i,j; for(i=1;i<=m;++i)scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].f); int re; for(re=1;;++re){ Stg.reset(); for(i=1;i<=m;++i)for(j=1;j<=re;++j)Stg.make(get(E[i].u,j),get(E[i].v,j+1),E[i].f); for(j=1;j<=re;++j)Stg.make(0,get(1,j),INF); for(j=2;j<=re+1;++j)Stg.make(get(n,j),n*(re+1)+1,INF); int now=Stg.Maxflow(0,n*(re+1)+1); //printf("%d\n",now); if(now>=T)break; } printf("%d\n",re); return 0; }
BZOJ2338:[HNOI2011]数矩形 暴力+计算几何
Feb 03, 2015 08:58:01 AM
思路:
首先搞出所有的线段,然后中点相同的放在一起,并且长度相同的放在一起.
那么,中点相同并且长度相同,这两个线段就能够构成一个矩形.
这样的线段不会超过\(O(n)\)条,所以我们可以直接暴力枚举.
(其实我觉得可以把所有的线段以中点为原点进行极角序排序,然后我们就是要找出两条线段使得它们之间的夹角尽可能接近90,这大概就可以线性扫描了.)
本代码完全避免了精度问题!速来点赞!
#include<cstdio> #include<cstring> #include<cctype> #include<climits> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; struct Point{ int x,y; Point(){} Point(int _x,int _y):x(_x),y(_y){} bool operator<(const Point&B)const{return(x!=B.x)?x<B.x:y<B.y;} bool operator!=(const Point&B)const{return(x!=B.x)||(y!=B.y);} Point operator+(const Point&B)const{return Point(x+B.x,y+B.y);} Point operator-(const Point&B)const{return Point(B.x-x,B.y-y);} }; LL Cross(const Point&a,const Point&b){return(LL)a.x*b.y-(LL)a.y*b.x;} LL Getans(Point p1,Point p2,Point q1,Point q2){ LL res=Cross(p1-q1,p1-q2); return res<0?-res:res; } int gcd(int a,int b){return(!b)?a:gcd(b,a%b);} struct Double{ int x,y; Double(){} Double(int _x,int _y):x(_x),y(_y){} bool operator<(const Double&B)const{return(LL)x*B.y<(LL)y*B.x;} }; #define N 1510 struct Segment{ Point P1,P2;LL dis; bool insequal(const Segment&B)const{ if(P1.x+P2.x!=B.P1.x+B.P2.x)return 0; if(P1.y+P2.y!=B.P1.y+B.P2.y)return 0; return 1; } bool operator<(const Segment&B)const{ if(P1.x+P2.x!=B.P1.x+B.P2.x)return P1.x+P2.x<B.P1.x+B.P2.x; if(P1.y+P2.y!=B.P1.y+B.P2.y)return P1.y+P2.y<B.P1.y+B.P2.y; return dis<B.dis; } bool operator==(const Segment&B)const{return insequal(B)&&dis==B.dis;} }S[N*N>>1];int id; int x[N],y[N],n; inline LL sqr(int x){ return(LL)x*x; } int main(){ scanf("%d",&n);register int i,j,k,p;for(i=1;i<=n;++i)scanf("%d%d",&x[i],&y[i]); for(i=1;i<=n;++i)for(j=i+1;j<=n;++j)++id,S[id].P1=Point(x[i],y[i]),S[id].P2=Point(x[j],y[j]),S[id].dis=sqr(x[i]-x[j])+sqr(y[i]-y[j]); sort(S+1,S+id+1); LL res=0; for(i=1;i<=id;i=j+1){ for(j=i;j!=id&&S[j]==S[j+1];++j); for(k=i;k<=j;++k)for(p=k+1;p<=j;++p)res=max(res,Getans(S[k].P1,S[k].P2,S[p].P1,S[p].P2)); } cout<<res<<endl; return 0; }
BZOJ3251:树上三角形 数学+暴力
Feb 02, 2015 08:00:15 AM
思路:
考虑不存在三角形的情况:也就是说,任意选择三个数,总有一个最大的数不小于剩下两个数的总和.
我们想到一个的数列:斐波那契数列.
那么权值均在题目给定数据范围内的数列有多长?至多有\(log(Max)\)项.
如果超过这些项,就会在两个数之间插入某一个数,那么这三个数就是必定满足三角形条件的.(有点意识流)
总之如果总共有不超过\(log(Max)\)个数暴力判定,否则直接输出\(Y\).
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<algorithm> using namespace std; #define N 100010 int head[N],next[N<<1],end[N<<1]; inline void addedge(int a,int b){static int q=1;end[q]=b,next[q]=head[a],head[a]=q++;} inline void make(int a,int b){addedge(a,b),addedge(b,a);} int w[N]; int pa[N][16],dep[N]; inline void dfs(int x,int fa){ for(int j=head[x];j;j=next[j])if(end[j]!=fa)dep[end[j]]=dep[x]+1,pa[end[j]][0]=x,dfs(end[j],x); } int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=15;i>=0;--i)if(dep[pa[x][i]]>=dep[y])x=pa[x][i]; if(x==y)return x; for(int i=15;i>=0;--i)if(pa[x][i]!=pa[y][i])x=pa[x][i],y=pa[y][i]; return pa[x][0]; } int seq[51],id; inline bool judge(int a,int b,int c){ long long A=a,B=b,C=c; return A+B>C&&B+C>A&&A+C>B; } int main(){ int n,m;scanf("%d%d",&n,&m);register int i,j,k;for(i=1;i<=n;++i)scanf("%d",&w[i]); int a,b;for(i=1;i<n;++i)scanf("%d%d",&a,&b),make(a,b); dep[1]=1,dfs(1,-1); for(j=1;j<=15;++j)for(i=1;i<=n;++i)pa[i][j]=pa[pa[i][j-1]][j-1]; int t; while(m--){ scanf("%d%d%d",&t,&a,&b); if(t==0){ int Lca=lca(a,b),nums=dep[a]+dep[b]-2*dep[Lca]+1; if(nums<=40){ for(id=0;a!=Lca;a=pa[a][0])seq[++id]=w[a]; for(;b!=Lca;b=pa[b][0])seq[++id]=w[b]; seq[++id]=w[Lca]; bool ok=0; for(i=1;i<=id;++i)for(j=i+1;j<=id;++j)for(k=j+1;k<=id;++k)if(judge(seq[i],seq[j],seq[k]))ok=1; if(ok)puts("Y");else puts("N"); }else puts("Y"); }else w[a]=b; } return 0; }
BZOJ1406:[AHOI2007]密码箱 数学+暴力
Feb 02, 2015 07:52:40 AM
思路:
令\(x\)是一个可行答案,有\(x^2\%n=1\),即\((x-1)(x+1)=kn(k\in{Z})\).
显然我们可以令\(x-1=k_1n_1,x+1=k_2n_2,k=k_1k_2,n=n_1n_2\).(交换一下也是可以的)
那么我们就可以枚举所有的\(n_1\),看一看他能够表达出的\(x+1\)或是\(x-1\)是不是一个合法答案.我们用一个哈希表来支持插入.最后输出即可.
但是我们发现枚举所有的\(n_1\)(n的约数)显然是要超时的.因此我们只枚举\(n_1\geq{\sqrt{n}}\)的\(n_1\).这样单次枚举的复杂度为\(\sqrt{n}\),而且这样的约数非常少,这样就可以通过了.
#include<cstdio> #include<cstring> #include<cctype> #include<iostream> #include<algorithm> using namespace std; static const int mod=(1e5)+7; struct HashSet{ int head[mod],v[200010],next[200010],ind; void reset(){ind=0,memset(head,-1,sizeof head);} void insert(int x){ int ins=x%mod; for(int j=head[ins];j!=-1;j=next[j])if(v[j]==x)return; v[ind]=x,next[ind]=head[ins],head[ins]=ind++; } }S; int n; void Solve(int x){ for(int d=x;d<n-1;d+=x)if((long long)d*(d+2)%n==0)S.insert(d+1); for(int d=x;d<=n;d+=x)if(d-2>=1&&(long long)d*(d-2)%n==0)S.insert(d-1); } int main(){ scanf("%d",&n); if(n==1)puts("NONE"); else{ S.reset(),S.insert(1); for(int i=1;i*i<=n;++i)if(n%i==0)Solve(max(i,n/i)); sort(S.v,S.v+S.ind); for(int i=0;i<S.ind;++i)printf("%d\n",S.v[i]); } return 0; }