[BZOJ 1715]虫洞
呜……这题现在才A
这道题就是给一个图让判断有没有负环,没什么难度
然后……我写邻接表竟然开小内存了……
尽管如此我感觉我写此题时的代码风格还是不错的,比较值得学习
代码:
/************************************************************** Problem: 1715 User: danihao123 Language: C++ Result: Accepted Time:92 ms Memory:920 kb ****************************************************************/ #include <cstdio> #include <queue> #include <cstring> #include <cctype> using namespace std; const int maxn=501,maxm=5201; namespace Graph{ int n; int first[maxn]; int next[maxm],to[maxm],dist[maxm]; int tot=0; inline void AddEdge(int u,int v,int d){ tot++; next[tot]=first[u]; first[u]=tot; to[tot]=v; dist[tot]=d; } inline void ClearGraph(){ memset(first,0,sizeof(first)); memset(next,0,sizeof(next)); memset(to,0,sizeof(to)); memset(dist,0,sizeof(dist)); tot=0; } int cnt[maxn]; bool inQueue[maxn]; int d[maxn]; bool SPFA(){ register int i,u; queue<int> Q; memset(cnt,0,sizeof(cnt)); memset(inQueue,0,sizeof(inQueue)); for(i=1;i<=n;i++){ d[i]=0; cnt[i]=1; inQueue[i]=true; Q.push(i); } while(!Q.empty()){ u=Q.front(); Q.pop(); inQueue[u]=false; for(i=first[u];i;i=next[i]){ if(d[to[i]]>d[u]+dist[i]){ d[to[i]]=d[u]+dist[i]; if(!inQueue[to[i]]){ Q.push(to[i]); inQueue[to[i]]=true; if(++cnt[to[i]]>n) return true; } } } } return false; } }; inline int readint(){ register char c; register int temp=0; c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)){ temp=temp*10+c-'0'; c=getchar(); } return temp; } int main(){ register int i,m,w,u,v,d; int F; F=readint(); while(F--){ Graph::n=readint(); m=readint(); w=readint(); Graph::ClearGraph(); for(i=1;i<=m;i++){ u=readint(); v=readint(); d=readint(); Graph::AddEdge(u,v,d); Graph::AddEdge(v,u,d); } for(i=1;i<=w;i++){ u=readint(); v=readint(); d=readint(); Graph::AddEdge(u,v,-d); } if(Graph::SPFA()) puts("YES"); else puts("NO"); } return 0; }
[BZOJ 3725]Matryca
很有意思的思考题。
这道题通过特殊情况我们可以猜想,假设最近两个不同色块距离为[tex]x[/tex](这是半闭半开序列长度),则答案为[tex]n-x+1[/tex]。然而事实就是如此。
代码:
/************************************************************** Problem: 3725 User: danihao123 Language: C++ Result: Accepted Time:216 ms Memory:1796 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=1000001; char buf[maxn]; int main(){ int n; register int i,now; char last_s=0; scanf("%s",buf); n=strlen(buf); register int ans=n; for(i=0;i<n;i++){ if(buf[i]!='*'){ if(last_s){ if(buf[i]!=last_s){ ans=min(ans,i-now); last_s=buf[i]; now=i; }else{ now=i; } }else{ last_s=buf[i]; now=i; } } } printf("%d\n",n-ans+1); return 0; }
[BZOJ 4325]斗地主
是的你没有看错!就是这道坑提!
这题基本是个半成品,歧义真的,真的,真的很大。
至于方法?我们注意到同样的牌能一起出就尽可能避免拆开。例如,四带二分成炸弹加两单点(对子)肯定不好。三带一或者三带二也如此。先考虑不出顺子的最优解,并据此进行深度限制,再考虑出顺子。
然后,DFS暴搜即可。
顺便讲一下坑点:
- 顺子可以有A。
- 三带一,三带二,四代二都可以有头。但是大小头要区别开。
代码:
/************************************************************** Problem: 4325 User: danihao123 Language: C++ Result: Accepted Time:24 ms Memory:820 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; int n; int ans; int cnt[5],hand[15]; void dfs(int x){ if(x>ans) return; int i,j,rest=0; memset(cnt,0,sizeof(cnt)); for(i=0;i<=14;i++){ cnt[hand[i]]++; } while(cnt[4]>0){ cnt[4]--; rest++; if(cnt[2]>=2) cnt[2]-=2; else if(cnt[1]>=2) cnt[1]-=2; } while(cnt[3]>0){ cnt[3]--; rest++; if(cnt[2]>0) cnt[2]--; else if(cnt[1]>0) cnt[1]--; } if(hand[0] && hand[1] && cnt[1]>=2) rest--; ans=min(ans,cnt[1]+cnt[2]+rest+x); for(i=3;i<=10;i++){ for(j=i;j<=14 && hand[j];j++){ hand[j]--; if(j-i+1>=5) dfs(x+1); } while(j>i) hand[--j]++; } for(i=3;i<=12;i++){ for(j=i;j<=14 && hand[j]>=2;j++){ hand[j]-=2; if(j-i+1>=3) dfs(x+1); } while(j>i) hand[--j]+=2; } for(i=3;i<=13;i++){ for(j=i;j<=14 && hand[j]>=3;j++){ hand[j]-=3; if(j-i+1>=2) dfs(x+1); } while(j>i) hand[--j]+=3; } } int main(){ int T,u,v; register int i; scanf("%d%d",&T,&n); while(T--){ ans=n; memset(hand,0,sizeof(hand)); for(i=1;i<=n;i++){ scanf("%d%d",&u,&v); if(!u){ if(v==2) hand[1]++; else hand[0]++; }else{ if(u==1) hand[14]++; else hand[u]++; } } dfs(0); printf("%d\n",ans); } return 0; }
[BZOJ 4152]The Captain
这题初看有些棘手。但是毕竟是最短路,[tex]min(abs(x_{1}-x_{2}),abs(y_{1}-y_{2}))[/tex]这样的边,大可以分成两条边来建,于是乎min就形同虚设了。并且我们可以看到这样建图有一个好处:跨越若干点的方案可以一定可以分成图上的若干边。问题大为简化,迎刃而解。
但这提有个丧病的地方:卡SPFA。加了一些优化照样挂。所以我一气之下写了可并堆优化的Dijkstra :)当然可并堆用的是pb_ds辣。
代码:
/************************************************************** Problem: 4152 User: danihao123 Language: C++ Result: Accepted Time:4888 ms Memory:17412 kb ****************************************************************/ #include <cstdio> #include <algorithm> #include <cstring> #include <utility> #include <ext/pb_ds/priority_queue.hpp> #include <cctype> #include <bitset> #ifdef DEBUG #include <cassert> #endif using namespace std; const int maxn=200001; int n; inline int abs(int x){ return x<0?-x:x; } int first[maxn]; int next[maxn*4],to[maxn*4],dist[maxn*4]; int graph_cnt=0; inline void Add_Edge(int x,int y,int d){ graph_cnt++; next[graph_cnt]=first[x]; first[x]=graph_cnt; to[graph_cnt]=y; dist[graph_cnt]=d; } int d[maxn]; bitset<maxn> vis; typedef pair<int,int> my_pair; typedef __gnu_pbds::priority_queue<my_pair,greater<my_pair> > Heap; Heap::point_iterator ite[maxn]; Heap Q; int dij(){ register int i,u; memset(d,0x7f,sizeof(d)); d[1]=0; ite[1]=Q.push(make_pair(0,1)); while(!Q.empty()){ u=Q.top().second; Q.pop(); if(vis[u]) continue; vis[u]=true; for(i=first[u];i;i=next[i]){ if(d[to[i]]>(dist[i]+d[u])){ d[to[i]]=dist[i]+d[u]; if(ite[to[i]]!=0) Q.modify(ite[to[i]],make_pair(d[to[i]],to[i])); else ite[to[i]]=Q.push(make_pair(d[to[i]],to[i])); } } } return d[n]; } int pr[maxn][2]; int order1[maxn],order2[maxn]; int cmp1(const int i,const int j){ return pr[i][0]<pr[j][0]; } int cmp2(const int i,const int j){ return pr[i][1]<pr[j][1]; } // I/O优化 inline int readint(){ char c=getchar(); register int x=0; while(!isdigit(c)) c=getchar(); while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); } return x; } int main(){ register int i; n=readint(); for(i=1;i<=n;i++){ pr[i][0]=readint(); pr[i][1]=readint(); order1[i]=i; order2[i]=i; } sort(order1+1,order1+1+n,cmp1); sort(order2+1,order2+1+n,cmp2); for(i=1;i<=n;i++){ if(i!=1){ Add_Edge(order1[i],order1[i-1],pr[order1[i]][0]-pr[order1[i-1]][0]); Add_Edge(order2[i],order2[i-1],pr[order2[i]][1]-pr[order2[i-1]][1]); } if(i!=n){ Add_Edge(order1[i],order1[i+1],pr[order1[i+1]][0]-pr[order1[i]][0]); Add_Edge(order2[i],order2[i+1],pr[order2[i+1]][1]-pr[order2[i]][1]); } } printf("%d\n",dij()); return 0; }
[BZOJ 1756]小白逛公园
这题是很经典的线段树题。
我们可以发现最长子序列或跨越中点,或不跨越中点。若不跨越则肯定在左子或右子中,跨越则是左子最大后缀和和右子最大前缀和之和。问题便迎刃而解。(当然,求区间最大前缀/后缀和要用上区间和)
需注意此题可能会出现a>b!
代码:
/************************************************************** Problem: 1756 User: danihao123 Language: C++ Result: Accepted Time:2604 ms Memory:49648 kb ****************************************************************/ #include <cstdio> #include <algorithm> using namespace std; const int maxn=500001; struct Node{ int L,R; int maxP,maxS,ans,sum; }; Node Tree[maxn*4]; int A[maxn]; void merge(Node& O,Node& LC,Node& RC){ O.sum=LC.sum+RC.sum; O.maxP=max(LC.maxP,LC.sum+RC.maxP); O.maxS=max(RC.maxS,RC.sum+LC.maxS); O.ans=max(max(LC.ans,RC.ans),LC.maxS+RC.maxP); } void maintain(int o){ Node& O=Tree[o],LC=Tree[o<<1],RC=Tree[o<<1|1]; merge(O,LC,RC); } void build_tree(int o,int L,int R){ Node& O=Tree[o]; O.L=L; O.R=R; if(L==R){ O.maxP=O.maxS=O.ans=O.sum=A[L]; }else{ int M=L+(R-L)/2; build_tree(o<<1,L,M); build_tree(o<<1|1,M+1,R); maintain(o); } } void update(int o,int p,int v){ Node& O=Tree[o]; int& L=O.L,R=O.R; if(L==R){ O.maxP=O.maxS=O.ans=O.sum=v; }else{ int M=L+(R-L)/2; if(p<=M) update(o<<1,p,v); else update(o<<1|1,p,v); maintain(o); } } int ql,qr; Node query(int o){ Node& O=Tree[o]; int& L=O.L,R=O.R; if(ql<=L && R<=qr){ return Tree[o]; } int M=L+(R-L)/2; Node ANS,LC,RC; if(ql<=M){ LC=query(o<<1); if(qr>M){ RC=query(o<<1|1); merge(ANS,LC,RC); return ANS; }else{ return LC; } }else{ RC=query(o<<1|1); return RC; } } int main(){ int n,m; register int i; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&A[i]); build_tree(1,1,n); int k,a,b; Node ans; for(i=1;i<=m;i++){ scanf("%d%d%d",&k,&a,&b); if(k&1){ if(a>b) swap(a,b); ql=a; qr=b; ans=query(1); printf("%d\n",ans.ans); }else{ update(1,a,b); } } return 0; }
[BZOJ 2429]聪明的猴子
图论填坑中……
这题不难,直接造图搞MST(严格讲是最小瓶颈生成树)即可。比较感人的是,Kruskal竟然也能过……
代码:
/************************************************************** Problem: 2429 User: danihao123 Language: C++ Result: Accepted Time:320 ms Memory:12592 kb ****************************************************************/ #include <cstdio> #include <algorithm> #include <cmath> using namespace std; const int maxn=1001,maxm=501; int n; struct Edge{ int u,v,d; bool operator < (const Edge& x) const{ return d<x.d; } }; // DJoinst Set int p[maxn],rank[maxn]; int find_set(int x){ if(p[x]==x) return x; else return p[x]=find_set(p[x]); } void link_set(int x,int y){ if(rank[x]>rank[y]){ p[y]=x; }else{ p[x]=y; if(rank[x]==rank[y]) rank[y]++; } } inline void union_set(int x,int y){ link_set(find_set(x),find_set(y)); } inline bool is_same(int x,int y){ return find_set(x)==find_set(y); } inline void init_set(){ for(register int i=1;i<=n;i++) p[i]=i; // memset(rank,0,sizeof(rank)); } // Kruskal int m=0; Edge E[maxn*maxn]; int MST(){ register int i,ans,count=0; init_set(); sort(E,E+m); for(i=0;i<m && count<=(n-1);i++){ if(!is_same(E[i].u,E[i].v)){ union_set(E[i].u,E[i].v); ans=E[i].d; count++; } } return ans; } int MK[maxm],Tree[maxn][2]; int main(){ int monkey; register int i,j,sd,ans=0; scanf("%d",&monkey); for(i=1;i<=monkey;i++) scanf("%d",&MK[i]); scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d%d",&Tree[i][0],&Tree[i][1]); for(i=1;i<=n;i++){ for(j=i+1;j<=n;j++){ E[m].u=i; E[m].v=j; E[m].d=hypot(abs(Tree[i][0]-Tree[j][0]),abs(Tree[i][1]-Tree[j][1])); m++; } } sd=MST(); for(i=1;i<=monkey;i++) if(MK[i]>=sd) ans++; printf("%d\n",ans); return 0; }
[BZOJ 3713]Iloczyn
这题应该注意到,斐波纳契函数增长速度很快,[tex]10^9[/tex]以下的斐波纳契函数值很少,所以可以打表。这样,问题就迎刃而解了。
代码:
/************************************************************** Problem: 3713 User: danihao123 Language: C++ Result: Accepted Time:20 ms Memory:828 kb ****************************************************************/ #include <cstdio> #include <cmath> #include <algorithm> using namespace std; int fib[46]={0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986,102334155,165580141,267914296,433494437,701408733,1134903170}; bool check(int n){ int m=sqrt(0.5+n); if(binary_search(fib,fib+46,n)) return true; register int i; for(i=2;i<=m;i++){ if(!(n%i) && binary_search(fib,fib+46,i)){ if(binary_search(fib,fib+46,n/i)) return true; } } return false; } int main(){ int T,n; scanf("%d",&T); while(T--){ scanf("%d",&n); if(check(n)) puts("TAK"); else puts("NIE"); } return 0; }
[BZOJ3043]IncDec Sequence
这题并不很好下手,但注意差分序列的前缀和为原值这个性质。
由此可见,所求数列的差分序列除了第一项以外都应该是0,求出满足条件的最小操作次数就轻松多了。
求满足条件的数列个数似乎也不是难事。通过差分序列易推数列第一项差分值的范围,突破口就在于此。
看起来,满足条件数列个数为[tex]max(S1,S2)-min(S1,S2)[/tex](S1,S2分别为正、负差分绝对值的和)。但是请注意,存在第一项没被操作的特殊情况。并且精度也是个问题!
/************************************************************** Problem: 3043 User: danihao123 Language: C++ Result: Accepted Time:292 ms Memory:820 kb ****************************************************************/ #include <cstdio> #include <algorithm> using namespace std; int main(){ int n; long long last=0,temp; register long long S1=0,S2=0,i,cf,ans2; scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%llu",&temp); cf=temp-last; if(i!=1){ if(cf>0) S1+=cf; else S2-=cf; } last=temp; } ans2=max(S1,S2)-min(S1,S2)+1; printf("%llu\n%llu\n",max(S1,S2),ans2); return 0; }
[BZOJ 1682]干草危机
这道题就是求最小瓶颈生成树中边的最大值。
然而图的MST就是图的一个最小瓶颈生成树……这下问题就迎刃而解了。
代码:
/************************************************************** Problem: 1682 User: danihao123 Language: C++ Result: Accepted Time:44 ms Memory:940 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define REP(i,n) for(i=0;i<n;i++) #define REPB(i,n) for(i=1;i<=n;i++) #define FROMG_TO E[i].u,E[i].v const int maxn=2001,maxm=10001; int n,m; // Djoint set int p[maxn],rank[maxn]; int find_set(int x){ if(p[x]==x) return x; else return p[x]=find_set(p[x]); } void link_set(int x,int y){ if(rank[x]>rank[y]){ p[y]=x; }else{ p[x]=y; if(rank[x]==rank[y]) rank[y]++; } } inline void union_set(int x,int y){ link_set(find_set(x),find_set(y)); } inline bool is_same(int x,int y){ return find_set(x)==find_set(y); } inline void init_set(){ register int i; for(i=1;i<=n;i++) p[i]=i; // memset(rank,0,sizeof(rank)); } // Graph struct Edge{ int u,v,d; }; int cmp(const Edge& a,const Edge& b){ return a.d<b.d; } Edge E[maxm]; int mst(){ register int i,cnt=0,ans=0; init_set(); sort(E,E+m,cmp); for(i=0;cnt<(n-1) && i<m;i++){ if(!is_same(FROMG_TO)){ union_set(FROMG_TO); ans=max(ans,E[i].d); cnt++; } } return ans; } int main(){ register int i; scanf("%d%d",&n,&m); REP(i,m){ scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].d); } printf("%d\n",mst()); return 0; }
[BZOJ 1232]安慰奶牛
这题要用到DFS欧拉遍历树(胡编词汇)的一个特征:每一条边都经过2遍。这样似乎每个边度数就可以确定了,然后……
但是,起点也要算啊……
代码:
/************************************************************** Problem: 1232 User: danihao123 Language: C++ Result: Accepted Time:248 ms Memory:2056 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxn=10001,maxm=100001; // Djoin set int p[maxn]; int find_set(int x){ if(p[x]==x) return x; else return p[x]=find_set(p[x]); } void union_set(int x,int y){ p[find_set(x)]=find_set(y); } bool is_same(int x,int y){ return find_set(x)==find_set(y); } // Graph struct Edge{ int u,v,d; }; int cmp(const Edge& a,const Edge& b){ return a.d<b.d; } int n,m; int c[maxn]; Edge E[maxm]; int mst(){ register int i,cnt=0,ans=0; for(i=1;i<=n;i++) p[i]=i; sort(E,E+m,cmp); for(i=0;cnt<(n-1) && i<m;i++){ if(!is_same(E[i].u,E[i].v)){ union_set(E[i].u,E[i].v); ans+=E[i].d; cnt++; } } return ans; } int main(){ register int i,kkk=0x5fffffff; scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&c[i]); kkk=min(kkk,c[i]); } for(i=0;i<m;i++){ scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].d); E[i].d+=E[i].d+c[E[i].u]+c[E[i].v]; } printf("%d\n",kkk+mst()); return 0; }