数据结构java
hdu4339(树状数组+二分)node
题意:给出长度分别为l1和l2的字符串s1和s2,要求进行k次操做,每次操做输入的第一个数若是是1,接着就输入两个整数a,i和一个字符c,要求将第a个字符串的下标为i的字符换位c。若是输入的第一个数是2,紧接着就输入一个数字i,要求找到一个最大的数字j,使得在区间[i,j)中两个字符串的字符相等。 分析:以前看到题目给了10s直接暴力求解TLE了。。。后来从别人的题解中受到了启发,须要用到树状数组,这样能节省很多时间开销。用一个数组保存状态(若是s1[i]=s2[i]就为0,不然为1),每次进行1操做时更新树状数组的数据,而进行2操做时用二分找到知足sum(j)-sum(i)=0的最大j就能够了。这道题又加深了我对树状数组的理解。 #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int maxn=1000005; char s1[maxn],s2[maxn]; int c[maxn]; int k,len; int lowbit(int x) { return x&-x; } void update(int i,int x)//更新树状数组的数据 { for(;i<=len;i+=lowbit(i)) c[i]+=x; } int sum(int i)//求和操做 { int sum=0; for(;i>0;i-=lowbit(i)) sum+=c[i]; return sum; } int Search(int i)//二分查找 { int left=i,right=len,mid=0,ans=i-1; while(left<=right) { mid=(left+right)/2; int x=sum(mid)-sum(i-1); //cout<<"mid="<<mid<<",sum(mid)="<<sum(mid)<<",sum(i-1)="<<sum(i-1)<<endl; if(x==0) { ans=mid; left=mid+1; } else right=mid-1; } return ans-i+1; } int main() { int T; cin>>T; int cnt=1; while(T--) { memset(c,0,sizeof c); printf("Case %d:\n",cnt++); scanf("%s%s",s1,s2); scanf("%d",&k); int len1=strlen(s1),len2=strlen(s2); len=min(len1,len2); for(int i=0;i<len;i++) { if(s1[i]!=s2[i]) { update(i+1,1); } } /*for(int i=1;i<=len;i++) printf("sum%d=%d ",i,sum(i));*/ while(k--) { int t; scanf("%d",&t); if(t==1) { int a,i; char ch; scanf("%d %d %c",&a,&i,&ch); bool flag=(s1[i]==s2[i]); if(a==1) s1[i]=ch; else s2[i]=ch; if(flag&&s1[i]!=s2[i]) { update(i+1,1); } if((!flag)&&s1[i]==s2[i]) { update(i+1,-1); } } else { int i; scanf("%d",&i); int ans=Search(i+1); printf("%d\n",ans); } } } return 0; }
hdu5737(有序表线段树)ios
解析:这是我之前没有接触过的线段树类型,有序表线段树,每一个节点申请了两段空间,主要是为了保存左边儿子会有多少比v小的,右边儿子会有多少比v小 的,因此在建树过程当中要归并排序。可能我讲起来比较难懂,详见代码,我给了注释。 代码 #include<cstdio> #include<cstdlib> #include<cstring> #include<string> #include<algorithm> using namespace std; typedef __int64 LL; #define e tree[id] #define lson tree[id*2] #define rson tree[id*2+1] const int mod=1e9+7; const int maxn=100005; const int C=~(1<<31),M=(1<<16)-1; int rnd(int last,int &a,int &b) { a=(36969+(last>>3))*(a&M)+(a>>16); b=(18000+(last>>3))*(b&M)+(b>>16); return (C&((a<<16)+b))%1000000000; } int n,m,A,B,a[maxn],b[maxn],c[maxn],xs[maxn]; bool cmp(const int& x,const int& y){ return b[x]<b[y]; } int data[maxn<<6],*p; int* New(int len){ p+=len; return p-len; } //静态的申请空间 void init() //离散化 { for(int i=0;i<n;i++) xs[i]=b[i]; sort(xs,xs+n); for(int i=0;i<n;i++) b[i]=upper_bound(xs,xs+n,b[i])-xs-1; for(int i=0;i<n;i++) a[i]=upper_bound(xs,xs+n,a[i])-xs-1; p=data; //在最开始的位置 } struct Tree { int le,ri,d,sum; int *lid,*rid; void Set(int v){ d=v; sum=v+1; } int GoLe(int v){ return v==-1?v:lid[v]; } //左边有多少<=v的 int GoRi(int v){ return v==-1?v:rid[v]; } //右边有多少<=v的 }tree[4*maxn]; void pushup(int id){ e.sum=lson.sum+rson.sum; } //取和 void pushdown(int id) { if(e.d!=-2) //延迟更新 { lson.Set(e.GoLe(e.d)); rson.Set(e.GoRi(e.d)); e.d=-2; } } void Build_tree(int id,int le,int ri) { e.le=le,e.ri=ri,e.d=-2; e.lid=New(ri-le+1); //左右都要申请空间 e.rid=New(ri-le+1); if(le==ri){ e.sum=(a[le]>=b[le]); return; } int mid=(le+ri)/2; Build_tree(id*2,le,mid); Build_tree(id*2+1,mid+1,ri); pushup(id); int ll=mid-le+1,rl=ri-mid; int *vl=b+le,*vr=b+mid+1; int i=0,j=0,cnt=0; while(i<ll&&j<rl) //归并排序 { if(vl[i]<vr[j]) //左边小于右边 { e.lid[cnt]=i; e.rid[cnt]=j-1; c[cnt++]=vl[i++]; } else { e.lid[cnt]=i-1; e.rid[cnt]=j; c[cnt++]=vr[j++]; } } while(i<ll) //左边没完的 { e.lid[cnt]=i; e.rid[cnt]=j-1; c[cnt++]=vl[i++]; } while(j<rl) //右边没完的 { e.lid[cnt]=i-1; e.rid[cnt]=j; c[cnt++]=vr[j++]; } int k=0; for(int i=le;i<=ri;i++) b[i]=c[k++]; } void Update(int id,int x,int y,int v) //更新 { int le=e.le,ri=e.ri; if(x<=le&&ri<=y){ e.Set(v); return; } //在区间内 pushdown(id); int mid=(le+ri)/2; if(x<=mid) Update(id*2,x,y,e.GoLe(v)); //左边GoLe表明左边有多少<=v的 if(y>mid) Update(id*2+1,x,y,e.GoRi(v));//右边同理 pushup(id); } int Query(int id,int x,int y) //查询 { int le=e.le,ri=e.ri; if(x<=le&&ri<=y) return e.sum; //在区间内直接返回值 pushdown(id); //延迟更新 int mid=(le+ri)/2; int ret=0; if(x<=mid) ret+=Query(id*2,x,y); //加上左边 if(y>mid) ret+=Query(id*2+1,x,y); //加上右边 return ret; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d%d",&n,&m,&A,&B); for(int i=0;i<n;i++) scanf("%d",&a[i]); //输入 for(int i=0;i<n;i++) scanf("%d",&b[i]); init(); Build_tree(1,0,n-1); //建树 int last=0,ret=0; for(int i=1;i<=m;i++) { int l=rnd(last,A,B)%n; int r=rnd(last,A,B)%n; int x=rnd(last,A,B)+1; if(l>r) swap(l,r); if((l+r+x)%2) //为奇数是插入 { x=upper_bound(xs,xs+n,x)-xs-1; Update(1,l,r,x); } else //不然查询 { last=Query(1,l,r); ret=(ret+(LL)i*last%mod)%mod; } } printf("%d\n",ret); } return 0; }
hdu5381(莫队算法)算法
解析: 莫队,先预处理出以i为右端点的区间的gcd值,有一些连续的区间的gcd值是相同的,好比[j,i],[j+1,i],[j+2,i]的gcd值是相同的,咱们能够把[j,j+2]这个 区间保存下来。同时也预处理出以i为左端点的,最后用莫队算法。详见代码实现。 代码 #include<cstdio> #include<vector> #include<cstring> #include<string> #include<cmath> #include<algorithm> using namespace std; const int maxn=10005; typedef __int64 LL; int N,M,A[maxn],block; struct Ques { int x,y,id; Ques(int x=0,int y=0,int id=0):x(x),y(y),id(id){} bool operator < (const Ques& t) const { int a=x/block,b=t.x/block; //排序 if(a!=b) return a<b; return y<t.y; } }ques[maxn]; int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } struct node { int id,g; node(int id=0,int g=0):id(id),g(g){} }; vector<node> vl[maxn],vr[maxn]; void GetLe() { for(int i=1;i<=N;i++) //获得以i为右端点连续区间的gcd值 { if(i==1){ vl[i].push_back(node(i,A[i])); continue; } int g=A[i],id=i; int Size=vl[i-1].size(); for(int j=0;j<Size;j++) { node& t=vl[i-1][j]; int ng=gcd(t.g,g); if(ng!=g) vl[i].push_back(node(id,g)); g=ng; id=t.id; } vl[i].push_back(node(id,g)); } } void GetRi() //同理 { for(int i=N;i>=1;i--) { if(i==N){ vr[i].push_back(node(i,A[i])); continue; } int g=A[i],id=i; int Size=vr[i+1].size(); for(int j=0;j<Size;j++) { node& t=vr[i+1][j]; int ng=gcd(t.g,g); if(ng!=g) vr[i].push_back(node(id,g)); g=ng; id=t.id; } vr[i].push_back(node(id,g)); } } LL WorkLe(int x,int y) { int Size=vl[y].size(); int ny=y; LL ret=0; for(int i=0;i<Size;i++) { node& t=vl[y][i]; if(t.id>=x) { ret+=(LL)t.g*(ny-t.id+1); ny=t.id-1; //跳过去 } else{ ret+=(LL)t.g*(ny-x+1); break; } } return ret; } LL WorkRi(int x,int y) { int nx=x; LL ret=0; int Size=vr[x].size(); for(int i=0;i<Size;i++) { node& t=vr[x][i]; if(t.id<=y) { ret+=(LL)t.g*(t.id-nx+1); nx=t.id+1; } else { ret+=(LL)t.g*(y-nx+1); break; } } return ret; } LL ans[maxn]; void solve() { for(int i=0;i<=N;i++) vl[i].clear(),vr[i].clear(); block=(int)sqrt(N+0.5); //分块 sort(ques,ques+M); //排序 GetLe(); //获得左边连续相同的gcd区间 GetRi(); //获得右边连续相同的gcd区间 int x=1,y=0; LL ret=0; for(int i=0;i<M;i++) //莫队的主要实现部分 { Ques& q=ques[i]; while(y<q.y){ y++; ret+=WorkLe(x,y); } while(y>q.y){ ret-=WorkLe(x,y); y--; } while(x<q.x){ ret-=WorkRi(x,y); x++; } while(x>q.x){ x--; ret+=WorkRi(x,y); } ans[q.id]=ret; //保存答案 } for(int i=0;i<M;i++) printf("%lld\n",ans[i]); //输出 } int main() { int T; scanf("%d",&T); while(T--) { scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&A[i]);//输入 scanf("%d",&M); int x,y; for(int i=0;i<M;i++) { scanf("%d%d",&x,&y); ques[i]=Ques(x,y,i); //离线保存查询 } solve(); } return 0; }
KMP算法数组
//参照《算法入门竞赛经典训练指南》 #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<iostream> using namespace std; void GetFail(char *P,int *fail) { int L=strlen(P); fail[0]=fail[1]=0; for(int i=1;i<L;i++) { int j=fail[i]; while(j&&P[i]!=P[j]) j=fail[j]; fail[i+1]=(P[i]==P[j]?j+1:0); } } void KMP(char *T,char *P,int *fail) { int L1=strlen(T); int L2=strlen(P); GetFail(P,fail); int j=0; for(int i=0;i<L1;i++) { while(j&&T[i]!=P[j]) j=fail[j]; if(T[i]==P[j]) j++; if(j==L2) printf("%d\n",i-L2+1); } } int main() { char T[30],P[20]; int fail[30]; while(scanf("%s%s",T,P)!=EOF) { KMP(T,P,fail); } return 0; }
后缀数组模板数据结构
//参照《算法入门竞赛经典训练指南》 #include<cstdio> #include<cstring> #include<string> #include<algorithm> using namespace std; const int maxn=1005; int wa[maxn],wb[maxn],wv[maxn],WS[maxn],sa[maxn]; bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void DA(int *r,int n,int m) //模板 { int i,j,p; int *x=wa,*y=wb; for(i=0;i<m;i++) WS[i]=0; for(i=0;i<n;i++) WS[x[i]=r[i]]++; for(i=1;i<m;i++) WS[i]+=WS[i-1]; for(i=n-1;i>=0;i--) sa[--WS[x[i]]]=i; for(p=1,j=1;p<n;j<<=1,m=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++) wv[i]=x[y[i]]; for(i=0;i<m;i++) WS[i]=0; for(i=0;i<n;i++) WS[wv[i]]++; for(i=1;i<m;i++) WS[i]+=WS[i-1]; for(i=n-1;i>=0;i--) sa[--WS[wv[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-1],sa[i],j)?p-1:p++; } } int a[maxn],b[maxn],rk[maxn],h[maxn]; void GetHeight(int *r,int n) { for(int i=0;i<=n;i++) rk[sa[i]]=i; int k=0; for(int i=0;i<n;i++) { if(k) k--; //先减1 int j=sa[rk[i]-1];//排名在前面的 while(r[i+k]==r[j+k]) k++; //相同一直加 h[rk[i]]=k; } } int main() { int N; while(scanf("%d",&N)!=EOF) { for(int i=0;i<N;i++) scanf("%d",&a[i]); a[N]=0; DA(a,N+1,N); GetHeight(a,N); printf("\n"); } return 0; }
ac自动机模板ide
//参照《算法竞赛入门经典训练指南》 #include<cstdio> #include<cstring> #include<string> #include<queue> #include<iostream> #include<algorithm> using namespace std; const int maxn=10005; const int cs=26; struct AC { int next[maxn][cs],val[maxn]; int id; int fail[maxn],last[maxn]; void init(){ memset(next[0],0,sizeof(next[0])); id=0; }//刚开始只初始化根节点 int GetId(char c){ return c-'a'; } void Insert(char *S,int I) //字典树插入字符串 { int s,be=0; for(int i=0;S[i]!='\0';i++) { s=GetId(S[i]); if(next[be][s]==0) { next[be][s]=++id; //增长新节点 val[id]=0; //置0 memset(next[id],0,sizeof(next[id])); //清空 } be=next[be][s]; } val[be]=I;//保存信息 } void GetFail() //找失配指针 { queue<int> que; fail[0]=0; for(int i=0;i<cs;i++) { int u=next[0][i]; fail[u]=last[u]=0; if(u) que.push(u); } while(!que.empty()) { int x=que.front(); que.pop(); for(int i=0;i<cs;i++) { int u=next[x][i]; if(!u){ next[x][i]=next[fail[x]][i]; continue; } que.push(u); int v=fail[x]; while(v&&!next[v][i]) v=fail[v]; fail[u]=next[v][i]; last[u]=val[fail[u]]?fail[u]:last[fail[u]]; } } } void Print(int i,int j) { if(!j) return; printf("%d: %d\n",i,val[j]); Print(i,last[j]); } void Find(char *T) //匹配 { int L=strlen(T); int j=0,s; for(int i=0;i<L;i++) { s=GetId(T[i]); j=next[j][s]; if(val[j]) Print(i,j); else if(last[j]) Print(i,j); } } }ac; int main() { int T; scanf("%d",&T); while(T--) { ac.init(); int N; scanf("%d",&N); char mom[50],son[10][20]; scanf("%s",mom); for(int i=1;i<=N;i++) { scanf("%s",son[i]); ac.Insert(son[i],i); } ac.GetFail(); ac.Find(mom); } return 0; }
poj3277(扫描线)函数
/* 题意:给出N个矩形,都在同一水平线上,求面积并 解析:裸的扫描线 */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<set> #include<map> #include<stack> #include<queue> #include<sstream> #include<utility> #include<iterator> using namespace std; const int INF=1e9+7; const double eps=1e-7; typedef __int64 LL; const int maxn=80005; int N,num,A[maxn]; struct node { int L,R,H,s; node(int L=0,int R=0,int H=0,int s=0) :L(L),R(R),H(H),s(s){} bool operator < (const node& t) const { return H<t.H; } }Nod[maxn]; struct Tree { int le,ri,lazy; int sum; }tree[4*maxn]; void build_tree(int id,int le,int ri) { Tree& e=tree[id]; e.le=le,e.ri=ri,e.lazy=0,e.sum=0; if(le==ri) return; int mid=(le+ri)/2; build_tree(id*2,le,mid); build_tree(id*2+1,mid+1,ri); return; } void pushup(int id) { Tree& fa=tree[id]; Tree& lson=tree[id*2]; Tree& rson=tree[id*2+1]; if(fa.lazy) fa.sum=A[fa.ri+1]-A[fa.le]; else if(fa.le==fa.ri) fa.sum=0; else fa.sum=lson.sum+rson.sum; } void update(int id,int x,int y,int c) { Tree& e=tree[id]; int le=e.le,ri=e.ri; if(x<=le&&ri<=y) { e.lazy+=c; pushup(id); return; } int mid=(le+ri)/2; if(x<=mid) update(id*2,x,y,c); if(y>mid) update(id*2+1,x,y,c); pushup(id); return; } LL solve() { LL ret=0; sort(Nod+1,Nod+num+1); sort(A+1,A+num+1); int k=1; for(int i=2;i<=num;i++) if(A[i]!=A[k]) A[++k]=A[i]; build_tree(1,1,k); for(int i=1;i<num;i++) { node& t=Nod[i]; int x=lower_bound(A+1,A+k+1,t.L)-A; int y=lower_bound(A+1,A+k+1,t.R)-A-1; update(1,x,y,t.s); ret+=(LL)tree[1].sum*((LL)Nod[i+1].H-Nod[i].H); } return ret; } int main() { while(scanf("%d",&N)!=EOF) { num=0; int a,b,h; for(int i=1;i<=N;i++) { scanf("%d%d%d",&a,&b,&h); Nod[++num]=node(a,b,0,1); A[num]=a; Nod[++num]=node(a,b,h,-1); A[num]=b; } LL ans=solve(); printf("%I64d\n",ans); } return 0; }
poj3225(线段树区间异或)ui
/* 题意:给出了几种集合运算,每次操做对一段区间进行其中的一种运算 输出最后被覆盖的区间段,要注意开区间与闭区间 解析:注意一下每种运算如何修改区间的值,在线段树里再增长两个变量 d(整个区间的值),c(是否须要异或),具体细节见代码。 */ #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iostream> #include<algorithm> #include<vector> #include<set> #include<map> #include<stack> #include<queue> #include<sstream> #include<utility> #include<iterator> using namespace std; const int INF=1e9+7; const double eps=1e-7; typedef __int64 LL; #define fa tree[id] #define lson tree[id*2] #define rson tree[id*2+1] #define e tree[id] const int maxn=140000; struct Tree { int le,ri,d,c; //d是区间的值,c表明是否须要异或 void UpXor() { if(d!=-1) d^=1; //是整个连续相同的 else c^=1; //懒惰标记异或 } }tree[4*maxn]; void build_tree(int id,int le,int ri) { e.le=le,e.ri=ri,e.d=0,e.c=0; if(le==ri) return; int mid=(le+ri)/2; build_tree(id*2,le,mid); build_tree(id*2+1,mid+1,ri); return; } inline void pushdown(int id) { if(fa.d!=-1) { lson.d=rson.d=fa.d; lson.c=rson.c=0; fa.d=-1; } if(fa.c) { lson.UpXor(); rson.UpXor(); fa.c=0; } } inline void Link(int id,char op) { if(op=='U') fa.d=1,fa.c=0; //并集 else if(op=='D') fa.d=0,fa.c=0; //差集S-T else if(op=='C'||op=='S') fa.UpXor(); //异或 } void update(int id,int x,int y,char op) { int le=e.le,ri=e.ri; if(x<=le&&ri<=y) { Link(id,op); return; } pushdown(id); int mid=(le+ri)/2; if(x<=mid) update(id*2,x,y,op); //左边可更新 else if(op=='I'||op=='C') lson.d=lson.c=0; //若是是交集或者是差集T-S,左边都要置为0 if(y>mid) update(id*2+1,x,y,op); //右边同理 else if(op=='I'||op=='C') rson.d=rson.c=0; return; } int mark[maxn]; //标记某位置是否被占 vector<pair<int,int> > ve; void query(int id) { if(e.le==e.ri) { mark[e.le]=e.d; return; } //一直压到叶子节点 pushdown(id); query(id*2); query(id*2+1); return; } int main() { char op,a,b; int x,y; build_tree(1,0,131070); //扩大了2倍 while(scanf("%c %c%d,%d%c",&op,&a,&x,&y,&b)!=EOF) { getchar(); x*=2; y*=2; //乘2 if(a=='(') x++; //不是实区间加1 if(b==')') y--; if(x>y) //区间为空 { if(op=='I'||op=='C') tree[1].c=tree[1].d=0; continue; } update(1,x,y,op); //更新 } memset(mark,0,sizeof(mark)); query(1); //整个压下去 ve.clear(); int pre=-1; for(int i=0;i<=131075;i++) { if(mark[i]) { if(pre!=-1) continue; pre=i; } else { if(pre==-1) continue; ve.push_back(make_pair(pre,i-1)); //找区间段 pre=-1; } } if(ve.size()==0) printf("empty set\n"); //为空 else { int kase=0; for(int i=0;i<ve.size();i++) //输出很恶心 { int L=ve[i].first; int R=ve[i].second; if(kase++) printf(" "); if(L%2) printf("("); else printf("["); printf("%d,%d",L/2,(R+1)/2); if(R%2) printf(")"); else printf("]"); } printf("\n"); } return 0; }
codeforces 145E(树状数组)spa
给你n个数,实现两种操做,一种操做是把一段区间的数增长某个值,另外一个操做是查询一段区间内的数知足"幸运数"要求的总个数,能够用树状数组实现. 由于题目已保证一个数的值不会超过10000,因此咱们能够先把1~10000的幸运数找出来,或者直接打一个表,这样查询一个数是不是幸运数的时间复杂度就是O(1) #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <iostream> #define LL long long using namespace std; int a[100001],c[100002],kk[10002]={0}; int maxn; inline int lowbit(int a) { return a&(-a); } void add(int x,int num) { while(x<=maxn) { c[x]+=num; x+=lowbit(x); } } int getsum(int x) { int sum=0; while(x) { sum+=c[x]; x-=lowbit(x); } return sum; } bool jug(int a) { while(a) { if(a%10!=4&&a%10!=7) return 0; a/=10; } return 1; } int main() { int i,ans,n,m,b,a1,c1,x1,x2; char s[10]; for(i=1;i<=10001;i++) if(jug(i)) kk[i]=1; while(~scanf("%d %d",&n,&m)) { maxn=n; for(i=0; i<=n+1; i++) c[i]=0; for( i=1; i<=n; i++) { scanf("%d",&a[i]); if(kk[a[i]]==1) add(i,1); } while(m--) { scanf("%s",s); if(s[0]=='c') { scanf("%d %d",&a1,&b); printf("%d\n",getsum(b)-getsum(a1-1)); } else { scanf("%d %d %d",&a1,&b,&c1); for(i=a1; i<=b; i++) { x1=kk[a[i]]; a[i]+=c1; x2=kk[a[i]]; if(x1==1&&x2==0) add(i,-1); else if(x1==0&&x2==1) add(i,1); } } } } return 0; }
Codeforces 276 E(在树上的操做)
题意: 给你一个无向图 除了 1号节点之外每一个节点的 度都是 2 即 一个入度一个出度 给你一些操做 输入0vxd 表示在距离v节点d内的全部节点都增长x 输入1v 表示查询v节点的值 关键就在于第一个操做的时候往上的节点会延伸到其余树链上去 这真尼玛麻烦 博客上给的思路和我一致 维护两种树 一个是在原树链上的操做 一个是操做延伸出来的 敲完代码A掉以后个人心里真的久久不能平静 可是仍是有一个地方不大懂 如有大神看到还望指点一下 Code #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> using namespace std; const int maxn=100005; int n,qnum,v,x,d,dis[maxn],root[maxn];//dis表示树的深度,root表示树的首节点,以这两个数组定位一个节点 vector<int> g[maxn];//实现无向图 vector<vector<long long> > tree;//树的实现 int lowbit(int x) { return x&(-x); } void add(int st,int en,int val,int cur) { if(st>en) return; int sz=tree[cur].size(); for(int i=st;i<sz;i+=lowbit(i)) tree[cur][i]+=val; for(int i=en+1;i<sz;i+=lowbit(i))//鶸表示这里不是很理解…………为何不能一个st~en循环呢………… tree[cur][i]-=val; } long long getsum(int dep,int cur) { long long ret=0; int sz=tree[cur].size(); for (int i=dep; i>0; i-=lowbit(i)) ret+=tree[cur][i]; return ret; } void dfs(int u,int pre,int dep,int cur)//dfs初始化 { dis[u]=dep; root[u]=cur; tree[cur].push_back(0);//每次push进一个0使得size增长,同时初始化每棵树 int sz=g[u].size(); for (int i=0; i<sz; i++) { int v=g[u][i]; if (v!=pre) dfs(v,u,dep+1,cur); } } int main() { while(scanf("%d%d",&n,&qnum)!=EOF) { int a,b; for(int i=0; i<=n; i++) g[i].clear(); for(int i=0; i<n-1; i++) { scanf("%d%d",&a,&b); g[a].push_back(b); g[b].push_back(a); } int sz=g[1].size(); tree.resize(sz+5); for(int i=0; i<sz; i++) { tree[i+1].push_back(0); dfs(g[1][i],1,1,i+1); } tree[0].resize(maxn,0);//对tree[0]初始化 int op; long long sum=0; while(qnum--) { scanf("%d",&op); if(op) { scanf("%d",&v); int cur=root[v]; if(v==1) printf("%I64d\n",sum); else printf("%I64d\n",getsum(dis[v],cur)+getsum(dis[v],0));//答案是由tree[0]与其原树相加结果 } else { scanf("%d%d%d",&v,&x,&d); int cur=root[v]; if(d<dis[v]) add(dis[v]-d,dis[v]+d,x,cur);//在一条树链上足以操做 else { sum+=x;//记录全部add 便于v==1时输出 add(1,dis[v]+d,x,cur);//在原树链上操做 add(1,d-dis[v],x,0);//上界操做由tree[0]负责 add(1,d-dis[v],-x,cur);//对原树链重复操做还原 } } } } return 0; }
hdu5316(线段树区间合并)
看一句:A beautiful subsequence is a subsequence that all the adjacent pairs of elves in the sequence have a different parity of position。 意思是说定义美丽子序列是区间里面的一个子序列,这个子序列中任意相邻的两个数在原序列中的具备不一样的奇偶性 两种操做,1.把序列x位置的值变成y;2.询问区间[x,y]里面的最大美丽子序列(即序列和最大) 区间合并的时候要知足当左子的美丽子序列若是是以奇位置结尾,那么右子的美丽子序列要以偶开头 若左边以偶结尾,右边则以奇开头 那么只须要保存区间内“偶始偶终”、“偶始奇终”、“奇始偶终”、“奇始奇终”四种美丽子序列,合并的时候奇偶配对着合并 用一个二维数组s[2][2]表示起点和终点的奇偶性,其中0表示偶,1表示奇 还有,其中最大的美丽子序列不能够是空集(意思是说,当最大美丽子序列为负的时候不要取0 。。。。) 1.#include<iostream> 2.#include<cstdio> 3.#include<algorithm> 4.using namespace std; 5.typedef long long ll; 6.const int N = 100000; 7.const ll inf = 1LL<<30LL; 8.struct Node 9.{ 10. int l,r; 11. ll s[2][2];//0偶1奇 12.}q[4*N]; 13.#define ls i<<1 14.#define rs i<<1|1 15.void pushup(int i) 16.{ 17. for (int j = 0;j <= 1;++j) 18. { 19. for (int k = 0;k <= 1;++k) 20. { 21. q[i].s[j][k] = max(q[ls].s[j][k],q[rs].s[j][k]); 22. q[i].s[j][k] = max(max(q[ls].s[j][0]+q[rs].s[1][k],q[ls].s[j][1]+q[rs].s[0][k]),q[i].s[j][k]); 23. } 24. } 25.} 26.void build(int i,int l,int r) 27.{ 28. q[i].l = l,q[i].r = r; 29. if (l == r) 30. { 31. ll y; 32. scanf("%I64d",&y); 33. for (int j = 0;j <= 1;++j) 34. { 35. for (int k = 0;k <= 1;++k) 36. { 37. q[i].s[j][k] = -inf; 38. } 39. } 40. if (l&1) //奇数 41. { 42. q[i].s[1][1] = y; 43. } 44. else q[i].s[0][0] = y; 45. return ; 46. } 47. int mid = (l+r)>>1; 48. build(ls,l,mid); 49. build(rs,mid+1,r); 50. pushup(i); 51.} 52.void update(int i,int x,ll y) 53.{ 54. if (x == q[i].l &&x == q[i].r) 55. { 56. if (x&1) //奇数 57. { 58. q[i].s[1][1] = y; 59. } 60. else q[i].s[0][0] = y; 61. return; 62. } 63. if (x <= q[ls].r) update(ls,x,y); 64. else update(rs,x,y); 65. pushup(i); 66.} 67.Node query(int i,int l,int r) 68.{ 69. if (l<=q[i].l&&q[i].r<=r) return q[i]; 70. int mid = (q[i].l+q[i].r)>>1; 71. if (r <= mid) return query(ls,l,r); 72. else if (l > mid) return query(rs,l,r); 73. else 74. { 75. Node ql = query(ls,l,mid); 76. Node qr = query(rs,mid+1,r); 77. Node ans; 78. for (int j = 0;j <= 1;++j) 79. { 80. for (int k = 0;k <= 1;++k) 81. { 82. ans.s[j][k] = max(ql.s[j][k],qr.s[j][k]); 83. ans.s[j][k] = max(max(ql.s[j][0]+qr.s[1][k],ql.s[j][1]+qr.s[0][k]),ans.s[j][k]); 84. } 85. } 86. return ans; 87. } 88.} 89.int main() 90.{ 91. int T;scanf("%d",&T); 92. while (T--) 93. { 94. int n,m;scanf("%d %d",&n,&m); 95. build(1,1,n); 96. int op;ll a,b; 97. for (int i = 0;i < m;++i) 98. { 99. scanf("%d",&op); 100. scanf("%I64d %I64d",&a,&b); 101. if (op == 0) 102. { 103. Node ans = query(1,(int)a,(int)b); 104. ll mx = - inf; 105. for (int j = 0;j <= 1;++j) 106. { 107. for (int k = 0;k <= 1;++k) 108. { 109. mx = max(ans.s[j][k],mx); 110. } 111. } 112. printf("%I64d\n",mx); 113. } 114. else if (op == 1) 115. { 116. update(1,(int)a,(ll)b); 117. } 118. } 119. } 120. return 0; 121.}
poj3486(线段树区间更新)
区间更新和区间查询有点相似 区间更新是由上往下的 每到一个点 判断这个点所表明的区间是否是在要更改的区间内 是的话在这个点更改这个点的数值标记 而且把这个点的值在原来值的基础增减(这个点区间范围*更改值 )而且返回 (不必定到最底层) 若是这个点自己的区间和他的子区间都不知足目标区间的范围 直接返回 不然 pushdown 再进入子区间进行判断 要注意返回后的路径上的值的更新 由于是自上而下的 因此一直到更新完目标区间 根节点到目标区间路径上点都被更新了 查询的时候 也是把查询路径上的点一路pushdown 因此最后的答案也是更新过的 1.#include<iostream> 2.#include<algorithm> 3.#include<cstdlib> 4.#include<cctype> 5.#include<cstdio> 6.#include<string> 7.#include<cstring> 8.#include<vector> 9.#include<set> 10.#include<map> 11.#include<queue> 12.#include<cmath> 13.#define pi acos(-1.0) 14.#define inf 1<<29 15.#define INF 0x3f3f3f3f 16.#define zero 1e-8 17. 18.const int li[] = { -1, 0, 1, 0}; 19.const int lj[] = {0, -1, 0, 1}; 20. 21.const int N = 1e5 + 10; 22. 23.using namespace std; 24. 25.struct node { 26. 27. long long data; 28. long long tag; 29. 30.} tree[N * 4]; 31. 32.int arr[N]; 33. 34.void build(int node, int Begin, int End) 35.{ 36. tree[node].tag = 0; 37. if (Begin == End) { 38. tree[node].data = arr[Begin]; 39. return; 40. } 41. build(node * 2, Begin, (Begin + End) / 2); 42. build(node * 2 + 1, (Begin + End) / 2 + 1, End); 43. tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data; 44.} 45.long long fin; 46. 47.void pushdown(int node, int l, int r) 48.{ 49. 50. tree[node * 2].tag += tree[node].tag; 51. tree[node * 2].data += tree[node].tag * ((l + r) / 2 - l + 1); 52. tree[node * 2 + 1].tag += tree[node].tag; 53. tree[node * 2 + 1].data += tree[node].tag * (r - (l + r) / 2); 54. tree[node].tag = 0; 55.} 56. 57.void update(int node, int b, int e, int l, int r, long long data) 58.{ 59. 60. if (l > e || r < b) return; 61. 62. if (l >= b && r <= e) { 63. tree[node].tag += data; 64. tree[node].data += data * (r - l + 1); 65. return; 66. } 67. 68. pushdown(node, l, r); 69. update(node * 2, b, e, l, (l + r) / 2, data); 70. update(node * 2 + 1, b, e, (l + r) / 2 + 1, r, data); 71. 72. tree[node].data = tree[node * 2].data + tree[node * 2 + 1].data; 73.} 74. 75. 76.void query(int node, int b, int e, int l, int r) 77.{ 78. 79. if (l > e || r < b) return; 80. 81. if (l >= b && r <= e) { 82. fin += tree[node].data; 83. return; 84. } 85. pushdown(node, l, r); 86. query(node * 2, b, e, l, (l + r) / 2); 87. query(node * 2 + 1, b, e, (l + r) / 2 + 1, r); 88.} 89.int main() 90.{ 91. 92. int n, q; 93. scanf("%d %d", &n, &q); 94. 95. for (int i = 1; i <= n; ++i) 96. scanf("%d", &arr[i]); 97. 98. build(1, 1, n); 99. 100. for (int i = 0; i < q; ++i) { 101. 102. char ch[10]; 103. scanf("%s", ch); 104. if (ch[0] == 'C') { 105. int a, b, c; 106. scanf("%d%d%d", &a, &b, &c); 107. update(1, a, b, 1, n, c); 108. } else if (ch[0] == 'Q') { 109. int a, b; 110. scanf("%d%d", &a, &b); 111. fin = 0; 112. query(1, a, b, 1, n); 113. printf("%lld\n", fin); 114. } 115. } 116. 117. return 0; 118.}
poj1177(扫描线求周长)
题意: 题目就是说n个矩形组成的新图形的周长,每一个矩形会给出左下角和右上角的坐标。 分析: 线段树+扫描线+离散化,我是从左到右扫描的,须要将纵坐标离散化,而后每扫描到一根线,求出其覆盖y方向的总长度,减去上次求得的长度,即为本条线增长或减小的长度,同时能够求出每两条线之间的距离,即为横坐标方向的周长的一部分,因为是矩形构成的,横坐标方向是为周长的部分必定成对出现,同时须要用到一个很重要的记录(num),由于可能出现两条竖线之间有4,6…甚至更多条外围的线段。 源代码: #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<queue> #include<stack> #include<algorithm> using namespace std; const int N=5005; typedef __int64 ll; struct seg { int y1,y2; int x; int flag; seg(){} seg(int _y1,int _y2,int _x,int f):y1(_y1),y2(_y2),x(_x),flag(f){} friend bool operator < (seg a,seg b) { if(a.x==b.x) return a.flag>b.flag; return a.x<b.x; } }Line[N<<1]; struct TNode { int l,r; int num;//子树中有多少条线段 int cover;//是否被覆盖,入边加1,出边-1 int len;//当前节点覆盖y方向的总长度 bool lb,rb;//左右节点是否被覆盖 }tre[N<<3]; vector<int>vec; void build(int o,int l,int r) { tre[o].l=l; tre[o].r=r; tre[o].num=0; if(l==r-1) return; int mid=(l+r)/2; build(o<<1,l,mid); build(o<<1|1,mid,r); } void update_len(int o) { if(tre[o].cover>0) { tre[o].len=vec[tre[o].r]-vec[tre[o].l]; tre[o].lb=tre[o].rb=true; tre[o].num=1; return; } if(tre[o].l==tre[o].r-1) { tre[o].cover=tre[o].lb=tre[o].rb=0; tre[o].num=tre[o].len=0; return; } tre[o].lb=tre[o<<1].lb; tre[o].rb=tre[o<<1|1].rb; tre[o].len=tre[o<<1].len+tre[o<<1|1].len; tre[o].num=tre[o<<1].num+tre[o<<1|1].num-(tre[o<<1].rb&tre[o<<1|1].lb); } void update(int o,seg p) { if(p.y1<=vec[tre[o].l]&&p.y2>=vec[tre[o].r]) { tre[o].cover+=p.flag; update_len(o); return; } if(tre[o].l==tre[o].r-1) return; int mid=(tre[o].l+tre[o].r)/2; if(p.y1<=vec[mid]) update(o<<1,p); if(p.y2>vec[mid]) update(o<<1|1,p); update_len(o); } int main() { int n; while(~scanf("%d",&n)) { int k=0; for(int i=0;i<n;i++) { int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2); Line[k++]=seg(y1,y2,x1,1);//进 //y[k]=y1; Line[k++]=seg(y1,y2,x2,-1);//出 //y[k]=y2; vec.push_back(y1); vec.push_back(y2); } sort(vec.begin(),vec.end()); vec.erase(unique(vec.begin(),vec.end()),vec.end()); sort(Line,Line+k); build(1,0,vec.size()-1); //printf("1111\n"); int ans=0; int old=0; int lines=0; for(int i=0;i<k;i++) { //printf("%d\n",Line[i].x); update(1,Line[i]); if(i>=1) { ans+=lines*2*(Line[i].x-Line[i-1].x); } ans+=abs(tre[1].len-old); old=tre[1].len; lines=tre[1].num; } printf("%d\n",ans); } return 0; }
搜索
hdu3812(深搜+剪枝)
题意: 给出N对字符串,每对字符串能够相连,问可否找到一条从sea到sky的字符串链,每一个字符串只能出现一次,若是能,输出 长度最长的,若是有多解,输出字典序最小的。无解输出what a pity 解析: 刚开始还觉得是个有环找最长路的图论题(然而我并不会写)。。。。。。看了别人题解才知道是深搜+剪枝。 对字符串排序标号,若是没有出现sea或者sky,直接无解,而后并查集一下,若是不在同一个集合,一样无解,而后判断每一个点 (起点和终点除外)是否能到达起点和终点,不能的在搜的过程当中直接无论,而后就是搜了,搜的过程当中更新解,若是最大长度 已经用完了全部的字符串就不必再搜了(最开始我没管这个超时了。。。。。。后来加了这个剪枝就过了) 代码 #include<cstdio> #include<cstring> #include<string> #include<map> #include<vector> #include<algorithm> #include<iostream> using namespace std; const int maxn=202; int N,id,be,en; string S1[maxn],S[maxn]; int d[maxn]; int root(int a){ return d[a]==a?a:d[a]=root(d[a]); } bool G[maxn][maxn]; int GetId(string& s) //找到下标 { for(int i=1;i<=id;i++) if(S[i]==s) return i; return 0; } bool input() { N*=2; for(int i=1;i<=N;i++) { cin>>S1[i]; S[i]=S1[i]; } sort(S+1,S+N+1); //排个序 be=en=0; //起点和终点编号 id=1; for(int i=2;i<=N;i++) { if(S[i]!=S[id]) S[++id]=S[i]; //去重 if(S[id]=="sea") be=id; if(S[id]=="sky") en=id; } if(!be||!en) return false; //没有sea或者sky for(int i=0;i<maxn;i++) d[i]=i; //并查集 memset(G,false,sizeof(G)); for(int i=1;i<N;i+=2) { int a=GetId(S1[i]); //下标 int b=GetId(S1[i+1]); int ra=root(a); int rb=root(b); G[a][b]=G[b][a]=true; //可连 if(ra!=rb) d[rb]=ra; //合并 } if(root(be)!=root(en)) return false; //不在同一集合 return true; } bool vis[maxn],tvis[maxn]; int maxl,temp[maxn],ans[maxn]; bool dfs(int x,int step) { vis[x]=true; temp[step]=x; if(x==en) //到终点 { if(step>maxl) //更新解 { maxl=step; for(int i=0;i<=maxl;i++) ans[i]=temp[i]; } if(step==id-1) return true; //用完全部的 } for(int i=1;i<=id;i++) if(!vis[i]&&G[x][i]) { if(dfs(i,step+1)) return true; } vis[x]=false; return false; } bool Reach(int x,int y) { if(x==y) return true; for(int i=1;i<=id;i++) if(!tvis[i]&&!vis[i]&&G[x][i]) { tvis[i]=true; if(Reach(i,y)) return true; } return false; } void solve() { memset(vis,false,sizeof(vis)); for(int i=1;i<=id;i++) { memset(tvis,false,sizeof(tvis)); tvis[be]=true; if(!Reach(i,en)) vis[i]=true; //可否到终点 } for(int i=1;i<=id;i++) { memset(tvis,false,sizeof(tvis)); tvis[en]=true; if(!Reach(i,be)) vis[i]=true; //可否到起点 } maxl=0; //最大长度 dfs(be,0); //深搜 } int main() { int T,Case=0; cin>>T; while(T--) { cin>>N; if(!input()) //无解 { printf("Case %d: what a pity\n",++Case); continue; } solve(); printf("Case %d:",++Case); for(int i=0;i<=maxl;i++) cout<<" "<<S[ans[i]]; cout<<endl; } return 0; }
hdu1401(双向bfs)
题意:棋盘大小为8*8,而后有4个球,给出初始全部球的位置以及目标位置,每次能够移动一个球,要么移动到旁边空的地方,要么跨过一个 球到空的地方(不能跨过多个球),问可否在8步之内到达目标状态。 解析:限定了8步,并且先后搜效果是同样的,因此能够考虑用双向bfs,而后8个数哈希(也能够开多维数组保存),要注意对4个点要排序。 代码 #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int mod=500007; const int maxn=200005; int dx[]={-1,0,1,0},dy[]={0,-1,0,1}; bool in(int x,int y){ return x>=1&&x<=8&&y>=1&&y<=8; } struct point //球的坐标 { int x,y; point(int x=0,int y=0):x(x),y(y){} bool operator < (const point& t) const { if(x!=t.x) return x<t.x; return y<t.y; } }; struct node { point p[4]; //每一个节点保存4个球的坐标,而且是排好序的 }nod[2][maxn]; //开二维用于双向搜 struct Hash { int v,k,nid,next; //保存哈希值,0或1,下标,next指针 }ha[mod+maxn]; int f[2],r[2],hash_id; //队首队尾指针 bool Same(int k1,int a,int k2,int b) //判断两个结构体是否彻底相同 { for(int i=0;i<4;i++) { if(nod[k1][a].p[i].x!=nod[k2][b].p[i].x) return false; if(nod[k1][a].p[i].y!=nod[k2][b].p[i].y) return false; } return true; } int GetHash(point p[]) //获得哈希值 { int ret=0,k=1; for(int i=0;i<4;i++) { ret+=p[i].x*k; k*=3; //k是系数 ret+=p[i].y*k; k*=3; } return ret; } int Insert_Hash(int v,int k,int nid) //插入 { int a=v%mod; int p=ha[a].next; while(p!=-1) { if(ha[p].v==v&&Same(ha[p].k,ha[p].nid,k,nid)) return ha[p].k==k?0:1; //有相同的状态,k值相同返回0,不一样返回1 p=ha[p].next; } p=++hash_id; ha[p].v=v; ha[p].k=k; ha[p].nid=nid; //增长新的节点 ha[p].next=ha[a].next; ha[a].next=p; return -1; } bool Has(node& t,int x,int y) { for(int i=0;i<4;i++) if(t.p[i].x==x&&t.p[i].y==y) return true; //此处是球 return false; } bool AddNode(node& t,int i,int j,int k) { int x=t.p[i].x; int y=t.p[i].y; int nx=x+dx[j]; int ny=y+dy[j]; if(!in(nx,ny)) return false; //出界 if(Has(t,nx,ny)) nx+=dx[j],ny+=dy[j]; //有球 if(!in(nx,ny)) return false; //出界 if(Has(t,nx,ny)) return false; //还有球 node& tt=nod[k][r[k]]; tt=t; tt.p[i].x=nx; tt.p[i].y=ny; sort(tt.p,tt.p+4); //排序 int a=Insert_Hash(GetHash(tt.p),k,r[k]); if(a==1) return true; //找到解 else if(a==-1) r[k]++; //增长新节点 return false; } bool bfs(int k) { int en=r[k]; while(f[k]<en) { node& t=nod[k][f[k]++]; for(int i=0;i<4;i++) //4个球4个方向 for(int j=0;j<4;j++) if(AddNode(t,i,j,k)) return true; } return false; } bool solve() { if(Same(0,0,1,0)) return true; //相同 int step=0; f[0]=f[1]=0; r[0]=r[1]=1; for(int i=0;i<mod;i++) ha[i].next=-1; hash_id=mod-1; Insert_Hash(GetHash(nod[0][0].p),0,0); Insert_Hash(GetHash(nod[1][0].p),1,0); while(f[0]<r[0]||f[1]<r[1]) { if(step>=4) return false; step++; if(bfs(0)) return true; if(bfs(1)) return true; } return false; } int main() { int x,y; while(scanf("%d%d",&x,&y)!=EOF) { nod[0][0].p[0]=point(x,y); for(int i=1;i<4;i++) { scanf("%d%d",&x,&y); nod[0][0].p[i]=point(x,y); } for(int i=0;i<4;i++) { scanf("%d%d",&x,&y); nod[1][0].p[i]=point(x,y); } sort(nod[0][0].p,nod[0][0].p+4); sort(nod[1][0].p,nod[1][0].p+4); if(solve()) printf("YES\n"); else printf("NO\n"); } return 0; }
模拟贪心乱搞yy
hdu3640(植物大战僵尸那题,模拟+二分)
题意:模拟植物大战僵尸,地图只有一行,有两种植物:豌豆射手(P)和炸弹土豆(M),僵尸要进攻,每只僵尸的hp是50,每一个豌豆射手的hp是10, 僵尸若是走在土豆上,土豆会爆炸,全部在土豆处的僵尸hp都变为0,若是僵尸走在豌豆射手处,则每只僵尸对豌豆射手攻击一次,形成1点的伤害, 豌豆射手会攻击走在最前面的僵尸,每一个豌豆射手攻击一次,形成1点的伤害。问最少须要多少只僵尸才能赢。 解析:很恶心的模拟,要注意的是僵尸不是一个接着一个排成队放在右边,能够几个僵尸放在同一个位置开始进攻,否则若是一开始就有超过50个 豌豆射手,那岂不是进来一个死一个(我最开始觉得是一个一个放,觉得不会有这种无解的状况。。。。。。),若是僵尸碰到的是炸弹,那么下一 回合能够认为僵尸的最左位置为炸弹的左边。若是碰到的不是炸弹,则不停向左统计植物个数,直到遇到第一个炸弹,而后二分能够吃掉这些植物 的最少僵尸个数。详见代码实现。 代码 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int maxn=105; char S[maxn]; int L,Pnum,Mnum,step; void init() { L=strlen(S)-1; Pnum=Mnum=0; //豌豆射手与土豆的个数 for(int i=0;i<=L;i++) if(S[i]=='P') Pnum++; else Mnum++; } bool judge(int totpnum,int pnum,int znum) { int nowstep=step; int P_hp=10,Z_hp=50; //豌豆射手和僵尸的hp while(pnum>0&&znum>0) { if(nowstep>0){ nowstep--; Z_hp-=totpnum; } //没走到豌豆射手处 else{ P_hp-=znum; Z_hp-=totpnum; } //走到了两个都要受伤害 if(P_hp<=0) //死一个射手 { P_hp=10; totpnum--; pnum--; nowstep=1; } if(Z_hp<=0){ Z_hp=50; znum--; } //死一个僵尸 } if(pnum<=0&&znum>0) return true; //全部豌豆射手挂掉而僵尸有多的 return false; } int BIS(int totpnum,int pnum) //二分 { int x=1,y=2*maxn,mid; while(x<=y) { mid=(x+y)/2; if(judge(totpnum,pnum,mid)) y=mid-1; else x=mid+1; } return y+1; } int solve() { int ret=0; //ret是须要的僵尸的个数 if(S[L]=='M') { ret++; L--; step=2; } //最右边是土豆,step表明走到下一个位置须要的步数 else step=1; while(L>=0) //知道小于0才算赢 { if(S[L]=='M') //是土豆 { if(step>1) //往前走 { step--; if(Pnum>=50) ret++; //超过50个豌豆射手死一个僵尸 continue; } else ret++; //炸死一个 Mnum--; L--; step=2; //step变为2 } else //若是是豌豆射手 { int pnum=0; for(int i=L;i>=0;i--) //一直到找到土豆 if(S[i]=='M') break; else pnum++; ret+=BIS(Pnum,pnum); //二分获得最小的僵尸个数 Pnum-=pnum; L-=pnum+1; //多减1表示把土豆算进去了,由于有没死的僵尸在土豆处牺牲 step=2; } } if(S[0]=='M') ret++; //若是最左边是土豆,则还须要一个僵尸去攻击首脑 return ret; } int main() { int T,Case=0; scanf("%d",&T); while(T--) { scanf("%s",S); //植物 init(); printf("Case %d: %d\n",++Case,solve()); } return 0; }
hdu3866(贪心)
题意:n人合伙买一件价格为p的礼物,每一个人有本身能承担的最大费用,要保证尽量公平,使付钱多的人和付钱少的人钱数的差距尽量小,若是存在矛盾,先来的人比后来的付钱多。 分析:要使尽量公平,能够先将总价格平分,若是有人能承担的最大费用比平均价格要少,则要支付本身所有的钱,而后剩余的钱由其它人循环按照此法解决,当最终剩余的人能承担的最大费用都比当前平均费用多时,则按照题目规则需多付钱的人比其余人少付1cent便可彻底解决问题。可见该题是一道贪心问题,可建立结点数组a用于保存每人能承担的最大费用s和本身的序号x,用数组b按序号保存没人须要支付的费用。 #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=10005; int p,n; bool vis[maxn]; //标记数组,若某结点已访问,则将其按序号标记为true struct node { int s,x; }a[maxn]; int b[maxn]; bool cmp(const node &a,const node &b) //将结点数组按s从小到大排序,若s相同则按x从小到大排序 { if(a.s<b.s) return true; else if(a.s==b.s) return a.x>b.x; return false; } int main() { int T; cin>>T; while(T--) { memset(vis,false,sizeof vis); scanf("%d%d",&p,&n); ll sum=0; for(int i=0;i<n;i++) { scanf("%d",&a[i].s); a[i].x=i; sum+=a[i].s; } if(sum<p) { puts("IMPOSSIBLE"); continue; } int nn=n; while(p) { sort(a,a+n,cmp); int ave=p/nn,mod=p%nn; int num=0; for(int i=0;i<n;i++) { if(vis[a[i].x]) continue; if(a[i].s<=ave) { b[a[i].x]=a[i].s; p-=a[i].s; vis[a[i].x]=true; num++; nn--; } else break; } if(num==0) { for(int i=n-1;i>=0;i--) { if(vis[a[i].x]) continue; if(mod) { b[a[i].x]=ave+1; mod--; } else b[a[i].x]=ave; } break; } } for(int i=0;i<n-1;i++) printf("%d ",b[i]); printf("%d\n",b[n-1]); } return 0; }
codeforces 239 div2 D
基本题意:总共有n+1个房间,每一个房间有两个门,在这里我假设为A门和B门,这两个门其中第一个只能回到前边几个房间,假设该房间为第i个房间,那么他只能回到对应的前边的1到i号房间(ps这里为何会有i号房间呢,后边一句来解释),具体是多少,得看输入的时候对应的是多少(也就是说若是你输入的就是i,那么也就是表明又再次回到原来的房间,回到同一个房间),第二道门是只可以到达下边一个房间,假设如今就在第i个房间,那么只能前往第i+1号房间,我在这里假设这两个房间为A门和 B门,A门往前边走,B门日后边走。并且一走进一个房间以后 ,这个房间就会有个计数器,这个计数器是只要你进去就会记一次数,若是计数以后为奇数(odd number),那么就只能走A门出去,即回到前边几个房间 数学公式的考察:这道题主要注意有个循环的过程,每次从一个门里边出来(若是你再次回到同一个房间,那么这个房间的计数器会变成奇数,(由于一开始是从这个房间的而A门出去的(ps由于你是经过回到同一个房间的,也就是说你是从A门走到1到i号房间的才到这歌儿同一个房间),那么也就是说那个时候计数器就是奇数,那么当你一进来那么就变成偶数了)这个时候就会走B门,到达那个i+1号房间)这就得出一个结论,就是只要你第一次走进一个房间(这个房间计数器变为奇数1),那么你必定是先走A门出去,前往前边几个房间(包括本身的房间),而后就再回来这个房间(这个房间计数器变成2(偶数)),而后就走B们出去走到i+1号房间最后要求你到达i+1号房间总共花多少步骤。上边说的都是理解题意得思路; 下边来讲解题思路:记s[i]为在第i号房间从B门出来前往i+1号门的时候所已经用过的步子数。所求的s[i]就是从i号房间的A们出去再回到这个房间所需的步子数加上回到这个房间以后从B门出去前往i+1号房间的步子数(固然这里包括了到达第i-1号房间已经走过的步子数)。最后求得就是s[n];s[i]=2*s[i-1]-s[a[i]-1]+2;这个就是递推关系式,下边简单解释一下,这个柿子变下心就是s[i]=(s[i-1]+1)+(s[i-1]-s[a[i]-1]+1),前边一个就是从第i-1号房间直接走到i号房间,而后直接走B门到i+1号房间,中间没有算A号门,即没有考虑从i号门回到前边的第a[i]号门,你理解一下下,后边一个柿子就是从第i号门经过走A号门回到第a[i]号门,而后再回来的所花的步子数,在这里有个小技巧,从第i号房间回到第a[i]号房间其实就等价于从a[i]-1号房间到达a[i]号房间的,也就是s[a[i]-1],这里读者好好细想一下,应该能够想明白的。 #include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <algorithm> #include <iostream> using namespace std; int n; int a[1005]; long long s[1005]; int main() { while(~scanf("%d",&n)) { for(int i=1; i<=n; i++) scanf("%d",&a[i]); memset(s,0,sizeof s); s[0]=0; for(int i=1;i<=n;i++) s[i]=(2*s[i-1]%1000000007-s[a[i]-1]%1000000007+1000000009)%1000000007; printf("%I64d\n",s[n]%1000000007); } return 0; }
hdu1112(模拟)
模拟 注意钥匙的左右边缘不能超过锁的范围 就算是空白也不行 1.#include<iostream> 2.#include<algorithm> 3.#include<cstdlib> 4.#include<cctype> 5.#include<cstdio> 6.#include<string> 7.#include<cstring> 8.#include<vector> 9.#include<queue> 10.#include<cmath> 11.#define pi acos(-1.0) 12.#define inf 1<<29 13.#define INF 0x3f3f3f3f 14.#define zero 1e-8 15. 16.using namespace std; 17. 18.char lock[10507][1207]; 19.char key[127][127]; 20.bool flag[1107][1207]; 21. 22.struct edge { 23. int x, y; 24.} lef[10007], rig[10007], down[10007]; 25. 26.int l, rr, d; 27.int r, c, n, m; 28.int depth; 29.int len; 30.void todo() 31.{ 32. l = 0; 33. r = 0; 34. d = 0; 35. len = 0; 36. 37. for (int i = 0; i < rr; ++i) 38. for (int j = 0; j < c; ++j) { 39. if (key[i][j] == '#') { 40. if (j > 0 && key[i][j - 1] == '#') continue; 41. lef[l].x = i; 42. lef[l++].y = j; 43. 44. } 45. } 46. for (int i = 0; i < rr; ++i) 47. for (int j = c - 1; j >= 0; --j) { 48. if (key[i][j] == '#') { 49. if (j < c - 1 && key[i][j + 1] == '#') continue; 50. rig[r].x = i; 51. rig[r++].y = j; 52. } 53. } 54. 55. for (int j = 0; j < c; ++j) 56. for (int i = rr - 1; i > -1; --i) { 57. if (key[i][j] == '#') { 58. if (i < rr - 1 && key[i + 1][j] == '#') continue; 59. down[d].x = i; 60. down[d++].y = j; 61. } 62. } 63. 64.} 65. 66.bool j_left(int x, int y) 67.{ 68. for (int i = 0; i < l; ++i) 69. if (y < 0 || lock[lef[i].x + x][lef[i].y + y] == '#') { 70. return false; 71. } 72. 73. return true; 74.} 75. 76.bool j_right(int x, int y) 77.{ 78. 79. for (int i = 0; i < r; ++i) { 80. if (c + y > m || lock[rig[i].x + x][rig[i].y + y] == '#') { 81. return false; 82. } 83. 84. } 85. 86. return true; 87.} 88. 89.bool j_down(int x, int y) 90.{ 91. 92. 93. for (int i = 0; i < d; ++i) { 94. 95. if (x > n + rr || lock[down[i].x + x][down[i].y + y] == '#') { 96. return false; 97. } 98. } 99. return true; 100.} 101. 102.void rem(int x, int y) 103.{ 104. 105. flag[down[0].x + x][down[0].y + y] = true; 106.} 107. 108.bool ifdo(int x, int y) 109.{ 110. 111. if (!flag[down[0].x + x][down[0].y + y]) return true; 112. return false; 113. 114.} 115.void dfs(int x, int y) 116.{ 117. 118. if (x > depth) depth = x; 119. if (depth == n + rr) return ; 120. rem(x, y); 121. 122. if (j_down(x + 1, y) && ifdo(x + 1, y)) dfs(x + 1, y); 123. if (j_left(x, y - 1) && ifdo(x, y - 1)) dfs(x, y - 1); 124. if (j_right(x, y + 1) && ifdo(x, y + 1)) dfs(x, y + 1); 125. 126.} 127. 128. 129.int main() 130.{ 131. 132. int t; 133. cin >> t; 134. for (; t--;) { 135. 136. scanf("%d %d", &rr, &c); 137. 138. for (int i = 0; i < rr; ++i) { 139. scanf("%s", key[i]); 140. } 141. 142. scanf("%d %d", &n, &m); 143. 144. memset(lock, '.', sizeof(lock)); 145. 146. for (int i = rr; i < rr + n; ++i) { 147. scanf("%s", lock[i]); 148. } 149. 150. 151. todo(); 152. depth = 0; 153. if (c > m) { 154. printf("The key falls to depth 0.\n"); 155. continue; 156. } 157. if (r == 0 && l == 0 && d == 0) { 158. printf("The key can fall through.\n"); 159. continue; 160. } 161. 162. memset(flag, false, sizeof(flag)); 163. 164. dfs(0, 0); 165. if (depth >= n + rr) printf("The key can fall through.\n"); 166. else 167. printf("The key falls to depth %d.\n", depth); 168. } 169. 170. return 0; 171.}
HDU5301
题意: 给axb大小的矩阵求出 不覆盖X点的能填满这个矩阵的 最大的矩阵的最小值 由于没有给n m谁大谁小, 先处理了,保证n是短边,m是长边.这样方便计算.(若是要改变的时候,记得将x,y也交换) 先无论X点,计算均分这个矩形的最小矩形的长度,就是最短边/2往上取整的那个数. 有特殊状况 若是 m==n && n是奇数 && x==y && x==(n+1)/2 这种状况 n是偶数的话 不会出现这样. 正常的话 是这样 (例 n=7 m=19 x=1 y=5) n!=m 时 就判断 x y的位置 来判断这个矩形的长度 并且 由于要最小 因此短边一直为1 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> #include<vector> using namespace std; int main() { int n,m,x,y; while (~scanf("%d%d%d%d",&n,&m,&x,&y)) { if (n>m) { swap(n,m); swap(x,y); } int ans=n/2; int half=n-ans; ans=max(ans,half); if (n==m && n%2 && x==y && x==(n+1)/2) ans--; else if (n!=m) { int h=max(x-1,n-x); int w=min(y-1,m-y)+1; if (w>ans) ans=max(ans,min(h,w)); } printf("%d\n",ans); } return 0; }
图论
hdu1384(差分约束)
题意:对于每一个区间[a,b]至少要有c个元素,问集合里元素的最小个数。 解析:差分约束,用Ti表示区间[0,i-1]有多少个元素在里面,则知足下面的条件 Tb+1-Ta>=c 0<=Ti+1-Ti<=1 则建边(a,b+1,c),(i,i+1,0),(i+1,i,-1) 而后用spfa求得答案。注意这题用vector可能会超时。 代码 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int INF=1e9+7; const double eps=1e-7; const int maxn=50005; int N; struct edge { int u,v,w,next; edge(int u=0,int v=0,int w=0):u(u),v(v),w(w){next=-1; } }E[maxn*3]; int head[maxn],dist[maxn]; bool inq[maxn]; queue<int> que; int spfa(int be,int en) { for(int i=be;i<=en;i++) dist[i]=-INF; dist[be]=0; memset(inq,false,sizeof(inq)); while(!que.empty()) que.pop(); que.push(be); while(!que.empty()) { int u=que.front(); que.pop(); inq[u]=false; for(int i=head[u];i!=-1;i=E[i].next) { int v=E[i].v,w=E[i].w; if(dist[v]<dist[u]+w) //更新 { dist[v]=dist[u]+w; if(!inq[v]){ inq[v]=true; que.push(v); } } } } return dist[en]; } int main() { while(scanf("%d",&N)!=EOF) { memset(head,-1,sizeof(head)); int u,v,w,cnt=0; int minv=INF,maxv=-INF; for(int i=0;i<N;i++) { scanf("%d%d%d",&u,&v,&w); //建边(u,v+1,w); v++; E[++cnt]=edge(u,v,w); E[cnt].next=head[u]; head[u]=cnt; minv=min(minv,u); maxv=max(maxv,v); } for(int i=minv;i<maxv;i++) { E[++cnt]=edge(i,i+1,0); //建边(i,i+1,0) E[cnt].next=head[i]; head[i]=cnt; E[++cnt]=edge(i+1,i,-1); //建边(i+1,i,-1) E[cnt].next=head[i+1]; head[i+1]=cnt; } printf("%d\n",spfa(minv,maxv)); } return 0; }
codeforces 14D
题意: 给你一个无向不成环的图 让你找出两条不相交的树链 使其乘积最大 思路 枚举每一条道路 并使其暂时断裂 再从这条道路的两端开始搜索 寻找最长的树链便可 说实话 搜索的题目虽说作的还凑合 可是这种给你一个连通方式 并以此造成无向图 再加以搜索的题目我仍是第一次作 我先想得是用二维矩阵保存路径 以G[u][v] 表示从 u到v是连通的 再用vector省点空间 以后我较为习惯的用了BFS 而后就进入了无限懵逼崩溃懵逼崩溃的状态 说到底用矩阵保存若是不用上结构体的话 很难记录链长 无奈仍是写了DFS #include <iostream> #include <cstdio> #include <vector> #include <algorithm> #include <cmath> #include <cstring> #include <queue> using namespace std; const int maxn=220; bool vis[maxn]; vector<int> G[maxn]; int ans; int dfs(int v,int x) { int sum=0; int max1=0,max2=0; for(int i=0; i<G[v].size(); i++) { if(G[v][i]!=x) { sum=max(sum,dfs(G[v][i],v));//sum获得从一个NODE出发的树的直径 循环操做获得最大 //与此同时 ans也在计算着从改点出发的最长树链 if(ans>max1) { max2=max1; max1=ans; } else max2=ans>max2?ans:max2; } } sum=max(sum,max1+max2);//最后sum获得从V出发的树的直径 ans=max1+1;//max1为该node的最长树链 由于递归返回 最大树链长度+1 return sum; } int main() { int n,a,b; while(scanf("%d",&n)!=EOF) { for(int i=0; i<=n; i++) G[i].clear(); for(int i=0; i<n-1; i++) { scanf("%d%d",&a,&b); G[a].push_back(b); G[b].push_back(a); } int last_ans=0; for(int i=0; i<n; i++) { int num=G[i].size(),temp; for(int j=0; j<num; j++) { int ans=0; temp=dfs(i,G[i][j])*dfs(G[i][j],i); last_ans=max(last_ans,temp); } } printf("%d\n",last_ans); } return 0; }
hdu5294(最短路+最大流)
题意: n个点,m条边,两我的,A在点1处,B在点n处,A必须走最短路到达n点(只走最短路,最短路可能多条) B切断图中的某些边不让A到达n点 问:1.B最少切断多少边使A不能到达n点 2.B最多切断多少边使A还能达到n点 分析: 问题1求最小割,根据最大流最小割定理,利用最大流求得,流量是1(边数) 对于问题2,只需在全部的最短路中找到边数最少的,而后用总边数m减去最少边的最短路上的边数 解法: 以给出的边权求最短路,根据最短路建图,判断边是否在最短路里面:d[v] - d[u] == w(u,v) 根据所建的新图,再求最短路(求最短路中的最少边数),每条边的权值变为1, 根据新图求最大流,流量为1(即最小割里的边权是1,是边数) 说明:代码很长不过都是模板,最大流的bfs、dfs模板均可以,最短路用的SPFA模板(很奇怪用Dijkstra怎么都过不了,不是超时而是WA。。。) 代码:dfs版增广路: 1.#define mem(a,x) memset(a,x,sizeof(a)) 2.#include<iostream> 3.#include<cstdio> 4.#include<cstring> 5.#include<algorithm> 6.#include<queue> 7.#include<set> 8.#include<stack> 9.#include<cmath> 10.#include<map> 11.#include<stdlib.h> 12.#include<cctype> 13.#include<string> 14.using namespace std; 15.typedef long long ll; 16.const int N = 2000; 17.const int inf = 1 << 27; 18.const int M = 120000; 19./****************************************最大流模板*********************************************/ 20.struct Edge 21.{ 22. int to;//终点 23. int cap;//容量 24. int rev;//反向边 25. Edge (int to = 0, int cap = 0, int rev = 0) : to (to), cap (cap), rev (rev) {} 26.}; 27.struct EdmondsKarp 28.{ 29. bool used[M + 5]; 30. vector<Edge>v[M + 5]; 31. void AddEdge (int from, int to, int cap) 32. { 33. v[from].push_back (Edge (to, cap, v[to].size() ) ); 34. v[to].push_back (Edge (from, 0, v[from].size() - 1) ); 35. } 36. int dfs (int s, int t, int f) 37. { 38. if (s == t) return f; 39. used[s] = 1; 40. for (int i = 0; i < v[s].size(); ++i) 41. { 42. Edge &tmp = v[s][i];//引用 43. if (!used[tmp.to] && tmp.cap > 0) 44. { 45. int d = dfs (tmp.to, t, min (f, tmp.cap) ); 46. if (d > 0) 47. { 48. tmp.cap -= d; 49. v[tmp.to][tmp.rev].cap += d; 50. return d; 51. } 52. } 53. } 54. return 0; 55. } 56. int MaxFlow (int s, int t) //求源点汇点的最大流 57. { 58. int flow = 0; 59. while (1) //一直循环直到找不到增广路 60. { 61. mem (used, 0); 62. int f = dfs (s, t, inf); 63. if (f == 0) return flow; 64. flow += f; 65. } 66. } 67.} q; 68./***************************************最短路模板*****************************************/ 69.struct Node 70.{ 71. int v, w; 72. Node (int v = 0,int w = 0):v(v),w(w){} 73.}; 74.struct Spfa 75.{ 76. int d[N+5]; 77. bool vis[N+5]; 78. queue<int>q; 79. vector<Node>u[N+5]; 80. void init(int n) 81. { 82. while (!q.empty()) q.pop(); 83. for (int i = 0;i <= n;++i) u[i].clear(); 84. mem(vis,0); 85. } 86. void AddEdge(int uu,int vv,int ww) 87. { 88. u[uu].push_back(Node(vv,ww)); 89. u[vv].push_back(Node(uu,ww)); 90. } 91. int spfa(int s, int t,int n) 92. { 93. for (int i = 0; i <= n; ++i) d[i] = inf; 94. q.push (s); 95. vis[s] = 1; 96. d[s] = 0; 97. while (!q.empty() ) 98. { 99. int h = q.front(); 100. q.pop(); 101. vis[h] = 0;//spfa不一样于bfs 102. for (int i = 0; i < u[h].size(); ++i) 103. { 104. int &v = u[h][i].v, &w = u[h][i].w; 105. if (d[v] > d[h] + w) 106. { 107. d[v] = d[h] + w; 108. if (!vis[v]) 109. { 110. q.push (v); 111. vis[v] = 1; 112. } 113. } 114. } 115. } 116. return d[t]; 117. } 118.} d,g; 119. 120./****************************************以最短路建图*********************************/ 121.void make_map (int n) 122.{ 123. for (int i = 1; i <= n; ++i) 124. { 125. for (int j = 0;j < d.u[i].size();++j) 126. { 127. int v = d.u[i][j].v, w = d.u[i][j].w; 128. if (d.d[v] - d.d[i] == w) 129. { 130. q.AddEdge(i,v,1);//最大流,流量是1 131. g.AddEdge(i,v,1);//最短路,边权是1 132. } 133. } 134. } 135.} 136./************************************************************************************/ 137.int main() 138.{ 139. int n, m; 140. while (~scanf ("%d %d", &n, &m) ) 141. { 142. d.init (n); 143. g.init (n); 144. mem (q.v, 0); 145. mem (q.used,0); 146. for (int i = 0, u, v, w; i < m; ++i) 147. { 148. scanf ("%d %d %d", &u, &v, &w); 149. d.AddEdge (u, v, w); 150. } 151. int minway = d.spfa (1, n, n); 152. make_map (n); 153. int ans1 = q.MaxFlow (1, n); 154. int ans2 = m - g.spfa (1, n, n); 155. printf ("%d %d\n", ans1, ans2); 156. } 157. return 0; 158.} 159./* 160. 161.7 12 162.1 2 1 163.2 7 8 164.1 7 8 165.1 6 7 166.6 7 1 167.2 3 3 168.3 6 3 169.6 4 1 170.7 4 2 171.1 5 2 172.5 4 4 173.7 5 6 174. 175.*/ bfs版的增广路: 1.#define mem(a,x) memset(a,x,sizeof(a)) 2.#include<iostream> 3.#include<cstdio> 4.#include<cstring> 5.#include<algorithm> 6.#include<queue> 7.#include<set> 8.#include<stack> 9.#include<cmath> 10.#include<map> 11.#include<stdlib.h> 12.#include<cctype> 13.#include<string> 14.using namespace std; 15.typedef long long ll; 16.const int N = 2000; 17.const int inf = 1<<27; 18.struct Node 19.{ 20. int v, w; 21. Node (int v = 0,int w = 0):v(v),w(w){} 22.}; 23.struct Spfa 24.{ 25. int d[N+5]; 26. bool vis[N+5]; 27. queue<int>q; 28. vector<Node>u[N+5]; 29. void init(int n) 30. { 31. while (!q.empty()) q.pop(); 32. for (int i = 0;i <= n;++i) u[i].clear(); 33. mem(vis,0); 34. } 35. void AddEdge(int uu,int vv,int ww) 36. { 37. u[uu].push_back(Node(vv,ww)); 38. u[vv].push_back(Node(uu,ww)); 39. } 40. int spfa(int s, int t,int n) 41. { 42. for (int i = 0; i <= n; ++i) d[i] = inf; 43. q.push (s); 44. vis[s] = 1; 45. d[s] = 0; 46. while (!q.empty() ) 47. { 48. int h = q.front(); 49. q.pop(); 50. vis[h] = 0;//spfa不一样于bfs 51. for (int i = 0; i < u[h].size(); ++i) 52. { 53. int &v = u[h][i].v, &w = u[h][i].w; 54. if (d[v] > d[h] + w) 55. { 56. d[v] = d[h] + w; 57. if (!vis[v]) 58. { 59. q.push (v); 60. vis[v] = 1; 61. } 62. } 63. } 64. } 65. return d[t]; 66. } 67.} d,g; 68.struct Edge 69.{ 70. int from, to, cap, flow; 71. Edge (int u = 0, int v = 0, int c = 0, int f = 0) : from (u), to (v), cap (c), flow (f) {} 72.}; 73.struct EdmondsKarp 74.{ 75. int n, m; 76. vector<Edge>edges;//边数的两倍 77. vector<int>G[N+5];//邻接表,G[i][j]表示节点i的第j条边在e数组中的序号 78. int a[N+5];//当起点到i的可改进量 79. int p[N+5];//最短路树上p的入弧编号 80. void init (int n) 81. { 82. for (int i = 0; i <= n; ++i) G[i].clear(); 83. edges.clear(); 84. } 85. void AddEdge (int from, int to, int cap) 86. { 87. edges.push_back (Edge (from, to, cap, 0) ); 88. edges.push_back (Edge (to, from, 0, 0) ); //反向弧 89. m = edges.size(); 90. G[from].push_back (m - 2); 91. G[to].push_back (m - 1); 92. } 93. int MaxFlow (int s, int t) 94. { 95. int flow = 0; 96. while (1) 97. { 98. mem (a, 0); 99. queue<int>Q; 100. Q.push (s); 101. a[s] = inf; 102. while (!Q.empty() ) 103. { 104. int x = Q.front(); 105. Q.pop(); 106. for (int i = 0; i < G[x].size(); ++i) 107. { 108. Edge & e = edges[G[x][i]]; 109. if (!a[e.to] && e.cap > e.flow) 110. { 111. p[e.to] = G[x][i]; 112. a[e.to] = min (a[x], e.cap - e.flow); 113. Q.push (e.to); 114. } 115. } 116. if (a[t]) break; 117. } 118. if (!a[t]) break; 119. for (int u = t; u != s; u = edges[p[u]].from) 120. { 121. edges[p[u]].flow += a[t]; 122. edges[p[u] ^ 1].flow -= a[t]; 123. } 124. flow += a[t]; 125. } 126. return flow; 127. } 128.}q; 129.void make_map (int n) 130.{ 131. for (int i = 1; i <= n; ++i) 132. { 133. for (int j = 0;j < d.u[i].size();++j) 134. { 135. int v = d.u[i][j].v, w = d.u[i][j].w; 136. if (d.d[v] - d.d[i] == w) 137. { 138. q.AddEdge(i,v,1);//最大流,流量是1 139. g.AddEdge(i,v,1);//最短路,边权是1 140. } 141. } 142. } 143.} 144.int main() 145.{ 146. int n, m; 147. while (~scanf ("%d %d", &n, &m) ) 148. { 149. d.init (n); 150. g.init (n);q.init(n); 151. for (int i = 0, u, v, w; i < m; ++i) 152. { 153. scanf ("%d %d %d", &u, &v, &w); 154. d.AddEdge (u, v, w); 155. } 156. int minway = d.spfa (1, n, n); 157. make_map (n); 158. int ans1 = q.MaxFlow (1, n); 159. int ans2 = m - g.spfa (1, n, n); 160. printf ("%d %d\n", ans1, ans2); 161. } 162. return 0; 163.} 164./* 165. 166.7 12 167.1 2 1 168.2 7 8 169.1 7 8 170.1 6 7 171.6 7 1 172.2 3 3 173.3 6 3 174.6 4 1 175.7 4 2 176.1 5 2 177.5 4 4 178.7 5 6 179.*/
codeforces 14D
题意: 本题目就是要求出一棵树上面两条路径长度的乘积,输出最大的乘积便可。 思路: 枚举每条边,对于每一条边,我均可以求出断开这条边之后获得的两条最长的路径的长度。其中的最大值就是答案。 代码 #include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; vector<int >ve[300]; int n; int vis[300]; int dis[300]; int tar[300]; int zux,zuy; bool ok(int x,int y) { if(x==zux&&y==zuy) return 1; else if(x==zuy&&y==zux) return 1; return 0; } void dfs(int num) { tar[num]=1; vis[num]=1; for(int i=0;i<ve[num].size();i++) { int x=ve[num][i]; if(vis[x]||ok(x,num)) continue; dis[x]=dis[num]+1; dfs(x); } } int pac(int x) { memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); dfs(x); int ans=0; for(int i=1;i<=n;i++) { if(dis[ans]<dis[i]) ans=i; } memset(dis,0,sizeof dis); memset(vis,0,sizeof vis); dfs(ans); ans=0; for(int i=1;i<=n;i++) { if(ans<dis[i]) ans=dis[i]; } return ans; } int solve() { memset(tar,0,sizeof tar); int t1=0,t2=0; if(ve[zux].size()==1) t1=0; else t1=pac(zux); if(ve[zuy].size()==1) t2=0; else t2=pac(zuy); return t1*t2; } int main() { while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) ve[i].clear(); int x,y; for(int i=0;i<n-1;i++) { scanf("%d%d",&x,&y); ve[x].push_back(y); ve[y].push_back(x); } int ans=0; for(int i=1;i<=n;i++) { for(int j=0;j<ve[i].size();j++) { zux=i; zuy=ve[i][j]; ans=max(ans,solve()); } // printf("ans:%d\n",ans); } printf("%d\n",ans); } return 0; }
数论
hdu2161
题意:求某个数的最大质因子是第几个质数 思路:用筛法打个带标记的质数表便可 代码: #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<iomanip> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<vector> #include<set> #include<map> using namespace std; const int MAXN = 1000000; int str[MAXN + 10], prime[MAXN]; int Max(int a, int b) { return a>b ? a : b; } void init() { memset(str, 0, sizeof str); int cnt = 1; str[0] = 0; str[1] = 0; for (int i = 2; i <= MAXN; i++) { if (str[i] == 0) { str[i] = cnt; prime[cnt++] = i; } for (int j = 1; j < cnt && i * prime[j] <= MAXN; j++) { str[i * prime[j]] = Max(j, str[i]); if (i % prime[j] == 0) { break; } } } } int main() { init(); int n; while (~scanf("%d", &n)) { printf("%d\n", str[n]); } return 0; }
hdu3519(矩阵快速幂)
硬币全部可能排列状况为2^n,朝向相同硬币连续三个如下 的排列状况为a(1)=2,a(2)=4,a(3)=6,a(4)=10,观察或者推算得a(n)=a(n-1)+a(n-2); 那么连续三个或三个以上硬币的全部组合状况 为b(n)=2^n-a(n); 化为只含b(n)的递推式即 b(n)=b(n-1)+b(n-2)+2^(n-2);b(1)=0;b(2)=0;b(3)=2;b(4)=6;用矩阵快速幂可快速算得b(n); 代码 #include <iostream> #include <cstdio> #include <cstring> #include <cmath> using namespace std; const int mod=10007; struct node { int a[4][4]; node() { a[1][1]=a[1][2]=a[1][3]=a[2][1]=1; a[2][2]=a[2][3]=a[3][1]=a[3][2]=0; a[3][3]=2; } void init() //将其初始化为单位矩阵 { memset(a,0,sizeof(a)); for(int i=1; i<4; i++) a[i][i]=1; } }; node mul(node a,node b) //(a*b)%mod 矩阵乘法 { node ans; for(int i=1; i<4; i++) for(int j=1; j<4; j++) { ans.a[i][j]=0; for(int k=1; k<4; k++) ans.a[i][j]+=a.a[i][k]*b.a[k][j]; ans.a[i][j]%=mod; } return ans; } node power(node a,int n) //(a^n)%mod //矩阵快速幂 { node ans; ans.init(); while(n) { if(n%2)//n&1 ans=mul(ans,a); n/=2; a=mul(a,a); } return ans; } int main() { int i,j,n; while(~scanf("%d",&n)) { if(n<3){printf("0\n");continue;} node ans; ans=power(ans,n-2); printf("%d\n",(ans.a[1][3]*2)%mod); } return 0; }
SGU 106(扩展欧几里得)
这题交的时候要注意是%I64d 因此我直接用了cin cout /* 方程两边同时除以gcd(a,b).咱们假设aa=a/gcd(a,b),bb=b/gcd(a,b),nn=n/gcd(a,b) 因此方程两边同时除以gcd(a,b)后, 能够获得一个方程aa*x+bb*y=nn. 而且该方程aa*x+bb*y=nn的解x,y就是a*x+b*y=n的解 咱们只要求解出aa*x+bb*y=1的其中一个解,设这两个解为x0,y0. 那么aa*x+bb*y=nn的其中一个解解就是x0*nn,y0*nn. 接着,a*x+b*y=n的其中一个解解也就是x0*nn,y0*nn. a*(x0*nn)+b*(y0*nn)=n. a*(x0*nn+1*b)+b*(y0*nn-1*a)=n a*(x0*nn-1*b)+b*(y0*nn+1*a)=n. 继续扩展 a*(x0*nn+k*b)+b*(y0*nn-k*a)=n (k属于整数) nn=n/gcd(a,b). x=x0*nn+k*b y=y0*nn-k*a */ #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<string> #include<algorithm> #include<queue> #include<stack> #include<set> #include<map> #include<vector> using namespace std; long long gcd(long long a,long long b) { return b==0?a:gcd(b,a%b); } long long exgcd(long long a,long long b,long long &x,long long &y) { if (!b) { x=1; y=0; return a; } long long gcd=exgcd(b,a%b,x,y); long long temp=x; x=y; y=temp-a/b*y; return gcd; } long long upper(long long a,long long b)//往上取整 { if (a<=0) return a/b; return (a-1)/b+1; } long long lower(long long a,long long b)//往下取整 { if (a>=0) return a/b; return (a+1)/b-1; } long long get(long long l,long long r,long long d,long long &k1,long long &k2) { if (d<0) { l=-l; r=-r; d=-d; swap(l,r); } k1=upper(l,d); k2=lower(r,d); } int main() { long long a,b,c,x1,y1,x2,y2; while (cin>>a>>b>>c)//ax+by+c=0 >>ax+by=-c { bool flag=0; c=-c; cin>>x1>>x2>>y1>>y2; long long ans,x,y; if (!a && !b && !c) { cout<<(x2-x1+1)*(y2-y1+1)<<endl; continue; } else if (!a && !b) { cout<<0<<endl; continue; } else if (!a) { if (c%b || c/b<y1 || c/b>y2) ans=0; else ans=x2-x1+1; cout<<ans<<endl; continue; } else if (!b) { if (c%a || c/a<x1 || c/a>x2) ans=0; else ans=y2-y1+1; cout<<ans<<endl; continue; } /* 先处理了a,b,c为0的状况 */ long long g=gcd(a,b); if (c%g)//若是 c不是gcd(a,b)的倍数 无解 { cout<<0<<endl; continue; } a/=g; b/=g; c/=g;// c=c/g 上面已经将c变为-c exgcd(a,b,x,y); long long k1,k2,k3,k4; x*=c; y*=c; get(x1-x,x2-x,b,k1,k2); get(y1-y,y2-y,-a,k3,k4); // cout<<x<<" "<<y<<endl; ans=min(k2,k4)-max(k1,k3)+1; cout<<ans<<endl; } return 0; }
codeforces 300E
题意: 给定n个数(a1,a2……an),求p,其中p=n!,且p能够被(a1!*a2!*…….*an!)整除。 分析: 将a1!*a2!*……*an!分解成质因子相乘的形式,可是确定不可能一个数一个数分解,巧妙的运用从大数转换成小数的思想,求出各个质因子及其个数。再运用二分求解对于每一个质因子而言最小的nn,全部的nn取最大即为结果 源代码: #include<cstdio> #include<cstring> #include<string> #include<queue> #include<map> #include<cmath> #include<stack> #include<vector> #include<set> #include<iostream> #include<algorithm> using namespace std; const int N=1000005; typedef __int64 LL; LL num[N*10]; int p[N*10]; bool vis[N*10]; int prime[N]; int tot=0; LL sum; int ma; void init() { memset(vis,false,sizeof vis); for(int i=2; i<N*10; i++) { if(!vis[i]) { prime[tot++]=i; p[i]=i; } for(int j=0; j<tot&&i<=N*10/prime[j]; j++) { vis[i*prime[j]]=true; p[i*prime[j]]=prime[j]; if(i%prime[j]==0) break; } } } void cal() { for(int i=ma;i>=2;i--) { if(p[i]!=i) { num[p[i]]+=num[i]; num[i/p[i]]+=num[i]; } } } bool ok(LL m,int a,LL n) { LL res=0; while(m) { res+=m/a; m/=a; if(res>=n) return true; } return false; } LL BST(int a,LL n) { LL l=1,r=sum; LL ans=-1; while(l<=r) { LL mid=(l+r)/2; if(ok(mid,a,n)) { ans=mid; r=mid-1; } else l=mid+1; } return ans; } LL solve() { LL ans=1; for(int i=0;i<tot;i++) { if(num[prime[i]]) { ans=max(ans,BST(prime[i],num[prime[i]])); } else break; } return ans; } int main() { int n; init(); while(~scanf("%d",&n)) { memset(num,0,sizeof num); ma=0; sum=0; for(int i=0; i<n; i++) { int x; scanf("%d",&x); ma=max(ma,x); num[x]++; sum+=x; } for(int i=ma-1;i>=2;i--) { num[i]+=num[i+1]; } cal(); printf("%I64d\n",solve()); } return 0; }
Codeforces 696C(几率+组合数学+指数循环节)
题意:有三个杯子倒扣于桌面,放的位置编号1,2,3,刚开始2号地方的杯子里有一个key,如今每次操做为从一、3号位置上等几率选择一个与2号位置的杯子交换(换杯子,位置编号仍是不变,可认为左、中、右三个,好比3换到1就是右边的杯子换到中间),求n次操做后key在2号位子的杯子里的几率,结果用分数。 题解:每次都是从一、3中选一个,显然几率就是1/2,并且每次2号位置的杯子都会变化。那么dp[i][n]表示第n次操做后,key在i号位置的杯子中的几率。 显然三个杯子,i=1,2,3;把换成一维的:f1(n),f2(n),f3(n).而后递推: 对于f1(n):可能第n-1次后key就在1位置,那么第n次只能选3号,即1/2*f1(n-1);可能第n-1次后key在2号中,那么只能选1位置,即1/2*f2(n-1);只有这两个可能到达,因此f1(n)=1/2*(f1(n-1)+f2(n-1)); 对于f3(n),位置是对称的:f3(n)=f1(n); 相似1,f2(n)=1/2*(f1(n-1)+f3(n-1)); 咱们要求的是f2(n) 上面三个等式:f2(n)=1/2*(f2(n-1)+f2(n-2)); 这个式子用组合数学求解线性常系数递推式(母函数内容) 最后f2(n)= . 题目对取模要求比较变态,必须是分子分母分别取模前要求化为最简分式,最简后再分别取模。上面的式子问题在于分母的3,由于2^(n-1)与分子已经没有公因子能够消了。那么看分子里面有没有3这个因子能够消: 很巧的是:当n-1为奇数时,2^(n-1)%3=2,此时n为偶数,(-1)^n=1;因此(2^(n-1)+(-1)^n)%3=0,同理n-1位偶数时有一样的结论 也就是说分子必然能消掉3;既然能整除,那么就把(2^(n-1)+(-1)^n)/3总体当作分子,并且是能够用逆元去处理3的,这样处理后就是最简分式了 即 ,inv是逆元,分子分母都是对1e9+7取模 如今问题就是n很大(不知道这里能用java搞),幂很大就是用指数循环节: 要求各欧拉函数 注意定理使用的条件,n较小时直接快速幂; #pragma comment(linker, "/STACK:10240000,10240000") #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<set> #include<vector> #include<map> #include<stack> #include<cmath> #include<algorithm> using namespace std; const double R=0.5772156649015328606065120900; const int N=1e5+5; const int mod=1e9+7; const int INF=0x3f3f3f3f; const double eps=1e-8; const double pi=acos(-1.0); typedef long long ll; int n; ll a[N],ini,M; ll judge() { ll tm=mod+10; ll ans=1; for(int i=0;i<n;i++) { if(ans>tm/a[i]) return 0;//很大 ans*=a[i]; } return ans; } ll Pow(ll a,ll n) { ll ans=1; while(n) { if(n&1) ans=ans*a%mod; a=a*a%mod; n>>=1; } return ans; } void solve(ll mo,ll tag) { ll ans=1; for(int i=0;i<n;i++) { a[i]%=mo;//注意爆数据 ans=ans*a[i]%mo; } ans--; ans=(ans%mo+mo)%mo; if(ans==0) ans+=mo; ll down=Pow(2,ans); ll up=down+tag; up=up*ini%mod; cout<<up<<'/'<<down<<endl; } int main() { ini=333333336,M=1000000006;//事先求好逆元和欧拉值 while(cin>>n) { int tag=-1; for(int i=0;i<n;i++) { scanf("%I64d",&a[i]); if(a[i]%2==0) tag=1;// } ll all=judge(); if(all==0) {solve(M,tag);continue;}//幂很大 if(all==1) { cout<<"0/1"<<endl; continue; } ll down=Pow(2,all-1); ll up=(down+tag)*ini%mod; cout<<up<<'/'<<down<<endl; } return 0; }
Hdu 5728 PowMod(欧拉函数+指数循环节)
题意:只是那个k是无限个 题解:首先是算k:先了解两条性质,欧拉函数是积性函数; (1)积性函数性质:F(m1*m2)=F(m1)*F(m2),当且近当gcd(m1,m2)=1时成立; (2) ,其中p是n的素因子,且gcd(p,n/p)=1。这个用欧拉函数的素因子表达式很好证实。 有了这个再来算k,题目的n是很特殊的,它的每一个素因子的幂次都是1: 那么假设素数p是n的一个素因子,显然gcd(p,n/p)=1;关键是i,若是i中没有素因子p,那么就直接用积性性质。若是i%p==0,必然能够写成i=k*p;即倍数关系,不然i%p!=0; 因此分红两部分求: .....p是素数,素数的欧拉值就是p-1; 到这里前两和式是能够合并的,考虑和式的上下限,含义不一样,第二项的i表示的p的倍数i*p才是第一项i的含义,至关于第二项恰好把第一项补齐了,那么从1到m没有遗漏,并且第二项的i用第一项替换后里面也是n/p;最终 这是个e二元递归式:n/p和m/p当作总体,那么设原先求的为f(n,m),因此f(n,m)=(p的欧拉值)*f(n/p,m)+f(n,m/p); 每次枚举一个就够了,n每次要除以p,最多就是把它的每一个素因子除完。 第二部分k的超级幂:用欧拉的定理:指数循环节 每次往幂次上一层模就取一次欧拉值,只有1的欧拉值等一本身,其余数的欧拉值都是小于本身的,因此模会不断变小至1,显然对1取模结果就是0,因此无限就变得有限了 #include<cstdio> #include<cstring> #include<string> #include<iostream> #include<sstream> #include<algorithm> #include<utility> #include<vector> #include<set> #include<map> #include<queue> #include<cmath> #include<iterator> #include<stack> using namespace std; const int INF=1e9+7; const double eps=1e-7; const int N=1e7+5; const int M=1000000007; typedef long long ll; int pri[N],phi[N]; bool vis[N]; int tot; ll sum[N]; void init() { int n=N; tot=0; memset(vis,false,sizeof vis); phi[1]=1; for(int i=2;i<n;i++) { if(!vis[i]) { pri[tot++]=i; phi[i]=i-1; } for(int j=0;j<tot && i*pri[j]<n;j++) { vis[i*pri[j]]=true; if(i%pri[j]==0) { phi[i*pri[j]]=phi[i]*pri[j]; break; } else phi[i*pri[j]]=phi[i]*(pri[j]-1); } } sum[0]=0; for(int i=1;i<N;i++) sum[i]=(sum[i-1]+phi[i])%M; } ll Pow(ll a,ll n,ll mod) { ll ans=1; while(n) { if(n&1) { ans=ans*a%mod; // if(ans>=mod)ans=ans%mod; } a=a*a%mod; // if(a>=mod) a=a%mod; n>>=1; } if(ans==0) ans+=mod; return ans; } ll solve(ll k,ll mod) { if(mod==1) return mod; ll tmp=phi[mod]; ll up=solve(k,tmp); ll ans=Pow(k,up,mod); return ans; } int rear; int a[15]; void resolve(ll n) { for(int i=0;i<tot;i++) { if(!vis[n]) {a[rear++]=n;break;} if(n%pri[i]==0) { a[rear++]=pri[i]; n/=pri[i]; } } } ll f(int pos,ll n,ll m) { //pos即每一个素数,一次一个就好了 if(n==1) return sum[m];//n为1结果就是欧拉值的前缀和 if(m==0)return 0; return ((a[pos]-1)*f(pos-1,n/a[pos],m)%M+f(pos,n,m/a[pos]))%M; } int main() { init();//打表 ll n,m,p; while(~scanf("%I64d%I64d%I64d",&n,&m,&p)) { rear=0; resolve(n);//素因子分解 ll k=f(rear-1,n,m);//算k ll ans=solve(k,p); printf("%I64d\n",ans%p); } return 0; }
dp
HDU2845
题意:求在一个矩阵中能吃掉数字的最大和,当吃掉某个数字的时候,该数字的上一行(如果第0行则不处理)和下一行(如果最后一行不处理)和左右两个数字(同理)会消失 思路:先按吃一个则左右消失的规则处理出每一行能吃到的最大数字和(能够用DP),而后再按照吃一行上下两行消失的规则处理行的和 代码: #include<iostream> #include<cstdio> #include<cstdlib> #include<string> #include<iomanip> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<vector> #include<set> #include<map> using namespace std; const int MAXN = 200010; int n, m,str[MAXN]; int Max(int a,int b) { return a>b?a:b; } int main() { while (~scanf("%d%d", &n, &m)) { int a[MAXN], b[MAXN]; a[0] = b[0] = 0; for (int i = 1; i <= n; i++) { int temp; for (int j = 1; j <= m; j++) { scanf("%d", &temp); a[j]=Max(a[j-1],b[j-1]); b[j]=a[j-1]+temp; } str[i]=Max(a[m],b[m]); } a[0]=b[0]; for(int i=1;i<=n;i++) { a[i]=Max(a[i-1],b[i-1]); b[i]=a[i-1]+str[i]; } printf("%d\n",Max(a[n],b[n])); } return 0; }
po2677(双调欧几里得旅行商问题)
题意: 给出n个点,要求从最左端点严格向右走到最右端点,再从最右端点走回来 途中必须通过全部点(保证每一个点横坐标不一样),求最短路。 解题思路: 双调欧几里得旅行商问题,dp。将点按横坐标从小到大排序,dp[i][j]表示从j向左走到1,再从1走到i的最短路(i<j)(此过程当中1-j全部的点都被经历过)。 给出转移方程: dp[i][j]=dp[i][j-1]+dist(j-1,j) (i<=j-2); dp[j-1][j]=min(dp[j-1][j],dp[i][j-1]+dist(i,j)) (i<=j-2); 按此方式更新不会丢失最优解(每次只把j归入集合而不考虑j+1,j+2...) 最终获得dp[n][n]=dp[n-1][n]+dist(n-1,n); 代码: 1. #include<iostream> 2. #include<cstring> 3. #include<cmath> 4. #include<iomanip> 5. using namespace std; 6. const double INF=1.0*9999999; 7. struct Node 8. { 9. int x,y; 10. }; 11. Node a[1005]; 12. double dp[1005][1005]; 13. int n; 14. double dist(int i,int j) 15. { 16. return sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)); 17. } 18. int main() 19. { 20. while(~scanf("%d",&n)) 21. { 22. for(int i=1;i<=n;i++) 23. scanf("%d%d",&a[i].x,&a[i].y); 24. memset(dp,INF,sizeof dp); 25. dp[1][2]=dist(1,2); 26. for(int j=3;j<=n;j++) 27. { 28. for(int i=1;i<=j-2;i++) 29. dp[i][j]=dp[i][j-1]+dist(j-1,j); 30. dp[j-1][j]=INF; 31. for(int i=1;i<=j-2;i++) 32. dp[j-1][j]=min(dp[i][j-1]+dist(i,j),dp[j-1][j]); 33. } 34. dp[n][n]=dp[n-1][n]+dist(n-1,n); 35. cout<<setprecision(2)<<fixed<<dp[n][n]<<endl; 36. } 37. }
hdu2059
题意: 额,中文题不翻译。 解题思路: 看别人的博客才理解的,因为每一个加油站均可以选择加油和不加油,为了把全部状况考虑进去,不妨令dp[i]为从起点到第i个加油站所须要的最短期,那么dp[i]就应该是从0到i-1这些节点充满电而后直接跑到i的最短期。为何这样作不会丢失最优解?不妨考虑第4个节点,计算过程当中你求出了dp[2]+t2和dp[3]+t3,这就等于说你已经考虑了第3个节点充电或者不充电的状况, 并且此时的dp[2]是它所能取得最小值,同理,dp[1]+t1和dp[2]+t2就表示考虑了第2个节点充电或者不充电的状况,那么有没有考虑第2个节点不充电可是第三个节点充电的状况呢?这里看上去是没有,可是你在计算dp[3]的时候,就已经考虑好了第二个点究竟该不应充电,因此第2个节点不充电可是第三个节点充电的状况可能会包含于dp[3]+t3中,好绕口啊。。 1. #include<cstdio> 2. #include<cstring> 3. #include<string> 4. #include<iostream> 5. #include<sstream> 6. #include<algorithm> 7. #include<utility> 8. #include<vector> 9. #include<set> 10. #include<map> 11. #include<queue> 12. #include<cmath> 13. #include<iterator> 14. #include<stack> 15. using namespace std; 16. const double INF=9999999; 17. const double eps=1e-7; 18. const int mod=1000007; 19. const int maxn=50000; 20. double dp[maxn]; 21. int a[maxn]; 22. int main() 23. { 24. int l,n,c,t,vr,v1,v2; 25. while(cin>>l) 26. { 27. cin>>n>>c>>t; 28. cin>>vr>>v1>>v2; 29. for(int i=1;i<=n;i++) 30. cin>>a[i]; 31. memset(dp,INF,sizeof dp); 32. sort(a+1,a+1+n); 33. a[0]=0; 34. a[n+1]=l; 35. dp[0]=0; 36. for(int i=1;i<=n+1;i++) 37. { 38. for(int j=0;j<i;j++) 39. { 40. double tt; 41. if(c>a[i]-a[j]) 42. tt=1.0*(a[i]-a[j])/v1; 43. else 44. tt=1.0*c/v1+1.0*(a[i]-a[j]-c)/v2; 45. dp[i]=min(dp[i],dp[j]+tt); 46. } 47. dp[i]+=t;//默认充满电 48. } 49. 50. if(dp[n+1]-t>1.0*l/vr) 51. cout<<"Good job,rabbit!"<<endl; 52. if(dp[n+1]-t<1.0*l/vr) 53. cout<<"What a pity rabbit!"<<endl; 54. } 55. }
hdu5076(dp+状态压缩)
题意: 旅行商问题,给出n个位置,求从起点(0,0)出发遍历全部位置再回到原点得最短路。 解题思路: 1.DP+状态压缩,以二进制保存当前遍历状态,若是此状态下某个点未被通过,则用此状态转移到通过该店的状态,更新其值。 2.注意到n很小,DFS搜一遍~。 代码1: 1. #include <iostream> 2. #include <cstdio> 3. #include <cstring> 4. #include <algorithm> 5. #include <cmath> 6. using namespace std; 7. const int N=55; 8. const int INF=0x3f3f3f3f; 9. int dp[(1<<11)+15][N]; 10. int dist[N][N]; 11. struct Node 12. { 13. int x,y; 14. }p[N]; 15. int main() 16. { 17. int n,m,t; 18. int cnt; 19. while(cin>>n>>m) 20. { 21. 22. cnt=0; 23. for(int i=0; i<n; i++) 24. for(int j=0; j<n; j++) 25. { 26. cin>>t; 27. if(t||(i==0&&j==0)) 28. { 29. p[cnt].x=i; 30. p[cnt++].y=j; 31. } 32. } 33. for(int i=0; i<cnt; i++) 34. { 35. for(int j=i+1; j<cnt; j++) 36. dist[i][j]=dist[j][i]=abs(p[i].x-p[j].x)+abs(p[i].y-p[j].y); 37. dist[i][i]=0; 38. } 39. memset(dp,INF,sizeof(dp)); 40. dp[0][0]=0; 41. for(int i=0; i<(1<<cnt); i++) 42. { 43. for(int j=0; j<cnt; j++) 44. { 45. if(dp[i][j]==INF) 46. continue; 47. for(int k=0; k<cnt; k++) 48. { 49. if(i&(1<<k)) 50. continue; 51. dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+dist[j][k]); 52. } 53. } 54. } 55. cout<<dp[(1<<cnt)-1][0]<<endl; 56. } 57. }
uva10118(分析+状压)
题意:桌上有4堆糖果,每堆糖果高度不超过40,每颗糖果有一种颜色(一共20种,1,2,3...,20), 有一个篮子,一开始是空的,每当里面装有两颗颜色相同的糖果时,就能够从篮子里拿出这一对糖果。 若是篮子里的糖果数量为5个而且不能再拿时,篮子充满,游戏结束。问最多能拿走多少对糖果。 糖果堆上的全部糖果拿光了也算结束 样例: 5 1 2 3 4 1 5 6 7 2 3 3 3 4 9 8 6 8 7 2 1 解释: 每堆糖果高度为5, 从左往右为4堆糖果,数字表明颜色。堆顶在上。 结果: 8 一开始看到这个题会以为状态根本就没法表示,由于你须要保存每一堆的剩余层数,还须要保存篮子里的糖果数量和颜色,即便状态存下了,转移起来时间复杂度也过高了。 41^4* 21^5实在太大。 后来想一想根本不用保存这么多状态。由于保存了每堆剩余的层数就知道了拿掉的糖果,又由于篮子里同种颜色的糖果只能是0或者1(同色糖会被拿掉),因此能够推出篮子里糖果的状态。 将糖果编号改成0到19,而后用状压表示每一个状态(每堆剩余层数)对应篮子里的糖果。 #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<cmath> #include<algorithm> #include<vector> using namespace std; #define all(x) (x).begin(), (x).end() #define for0(a, n) for (int (a) = 0; (a) < (n); (a)++) #define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++) #define ysk(x) (1<<(x)) typedef long long ll; typedef pair<int, int> pii; const int INF =0x3f3f3f3f; const int maxn=40 ; int a[5][maxn+3]; int state[maxn+10][maxn+10][maxn+10][maxn+10]; int dp[maxn+3][maxn+3][maxn+3][maxn+3]; int n; int getNum(int &s)//计算篮子s里的糖果数量 { int cnt=0; for(int i=0;i<20;i++) if(s&ysk(i)) { cnt++; } return cnt; } void fix(int &x,int y)//更新dp值 { if(y>x) x=y; } int update(const int* hp,const int* h,int p )//从状态hp转移到状态h,在第p堆转移。 { int s,sp=state[hp[1] ][hp[2] ][hp[3] ][hp[4] ]; int k= a[p][hp[p]];//k表示拿掉糖果的颜色。 if(sp&ysk(k) ) { s=sp^ysk(k); fix( dp[h[1]][h[2] ][h[3] ][h[4] ], dp[hp[1] ][hp[2] ][hp[3]][hp[4] ]+1 ); } else{ int num=getNum(sp); if(num==4) return 0; s=sp^ysk(k); fix( dp[h[1]][h[2] ][h[3] ][h[4] ], dp[hp[1] ][hp[2] ][hp[3]][hp[4] ] ); } state[h[1]][h[2]] [h[3] ][h[4] ]=s; return 1; } int work() { int ans=0; state[n][n][n][n]=0; memset(dp,-1,sizeof dp); dp[n][n][n][n]=0; int h[5]; for(h[1]=n;h[1]>=0;h[1]-- ){ for(h[2]=n;h[2]>=0;h[2]--){ for(h[3]=n;h[3]>=0;h[3]--){ for(h[4]=n;h[4]>=0;h[4]--){ if(h[1]+h[2]+h[3]+h[4]==4*n) continue; // dp[h[1] ][h[2]][h[3]][h[4] ]=-1; for(int i=1;i<=4;i++) if( h[i]+1<=n ) { int hp[5]; memcpy(hp,h,sizeof hp); hp[i]++; if(dp[hp[1] ][hp[2] ][hp[3] ][hp[4] ]==-1) continue; update(hp,h,i); } ans=max(ans,dp[h[1] ][h[2]][h[3]][h[4] ]); } } } } return ans; } int main() { while(~scanf("%d",&n)&&n) { for(int h=n;h>=1;h-- ) { for(int i=1;i<=4;i++) { scanf("%d",&a[i][h]);//第i堆,第h层糖果颜色 a[i][h]--; } } printf("%d\n",work()); } return 0; } /* 5 1 2 3 4 1 5 6 7 2 3 3 3 4 9 8 6 8 7 2 1 对于这组样例的分析: 若是从左往右,高度依次为 0 0 4 5 能够发现这时是个死局 由于篮子里有5颗颜色各不相同的糖果。 能够认为该状态没法达到,由于即便达到了也不能进行后续转移,而且这个状态的最后一步是没有消除糖果的,因此这个状态 相比于前面转移到它的状态并不更优, 因此说能够认为该状态达不到,这对于结果毫无影响。 假如状态(0 ,0 ,4, 5)没法达到,那么状态(0,0,4,4)还须要继续计算 ,(0,0,4,4)是能够到达的。 从结果上看,(0,0,4,4)篮子里只有4颗糖 可是(0,0,4,4)不可能从(0,0,4,5)转移过来的,彻底能够从(1,0,4,4)转移过来。最后一步不一样,表明着拿取糖果的顺序不一样。 还有一点须要注意的是,初始化是,应该所有memset(dp,-1,sizeof dp) 不然可能会受到上一组样例的影响 好比说考虑结果(0,0,3,3),枚举这个状态的前驱,若是状态(0,0,3,4)在这个样例里是根本不可达的,甚至能达到的状态距离它 还相距甚远,可是在上一组样例里这个状态能够达到的,那么它的dp值就不为-1,会从上一个样例的状态(0,0,3,4)转移到这个样例的 (0,0,3,3),从而形成错误。 */