2015 8月 做题记录
1 HDU 4334(线性查找|暴力hash)
题目:给出五个集合,每个集合元素个数为n,现问是否有五个元素分别属于这五个集合且其和为0.
思路:给定两个排好序的序列,可以在线性时间内求出是否有两个数分别属于这两个集合且其和为一定值。方法是设置两个指针,线性扫描两个数组。本题可以把5个集合分为三组,枚举其中一组,在另外两组中线性查找。总时间复杂度应该是O(n^3).
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=205; ll s[5][205]; ll g1[maxv*maxv],g2[maxv*maxv]; ll *g3=s[4]; int T,n; int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ cin>>n; for(int i=0;i<5;i++){ for(int j=0;j<n;j++){ scanf("%lld",&s[i][j]); } } for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ g1[i*n+j]=s[0][i]+s[1][j]; g2[i*n+j]=s[2][i]+s[3][j]; } } sort(g1,g1+n*n); sort(g2,g2+n*n); sort(g3,g3+n); int h1=0,h2=n*n-1; bool f=0; for(int i=n-1;i>=0;i--){ ll tar=-g3[i]; int h1=0,h2=n*n-1; while(1){ while(h1<n*n&&g1[h1]+g2[h2]<tar){ h1++; } while(h2>=0&&g1[h1]+g2[h2]>tar){ h2--; } if(h1>=n*n||h2<0) break; if(g1[h1]+g2[h2]==tar){ f=1; break; } } } if(f) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
hash: 暴力建hash表,然后查找,之前没写过hash表结果当场写sb了...=_=||。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int mod=1234567; const ll oo=100700010001000100LL; ll hashset[mod]; void init(){ for(int i=0;i<mod;i++) hashset[i]=oo; } int get(ll x){ return (x%mod+mod)%mod; } void insert(ll x){ int idx=(x%mod+mod)%mod; while(hashset[idx]!=oo){ if(hashset[idx]==x) return; idx++; if(idx==mod) idx=0; } hashset[idx]=x; } bool find(ll x){ int idx=(x%mod+mod)%mod; while(hashset[idx]!=oo&&hashset[idx]!=x){ idx++; // if(get(hashset[idx])!=get(x)) return 0; if(idx==mod) idx=0; } return hashset[idx]==x; } ll s[5][205]; int T,n; int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ cin>>n; for(int i=0;i<5;i++){ for(int j=0;j<n;j++){ scanf("%lld",&s[i][j]); } } init(); for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ insert(s[0][i]+s[1][j]); } } bool f=0; for(int i=0;i<n;i++){ for(int j=0;j<n;j++){ for(int k=0;k<n;k++){ if(find(-s[2][i]-s[3][j]-s[4][k])){ f=1; goto lab; } } } } lab: if(f) cout<<"Yes"<<endl; else cout<<"No"<<endl; } return 0; }
题目:每个人到某点的距离为直线距离的立方,现在求直线上一点到其他点的距离之和最小。
思路:裸的三分算法应用。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define cu(x) (x)*(x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=5e4+300; int T,N; double x[maxv],w[maxv]; double cul(double y){ double ans=0; for(int i=0;i<N;i++){ ans+=w[i]*cu(abs(y-x[i])); } //cout<<ans<<endl; return ans; } double work(){ double l=-1e6,r=1e6; double ansl=cul(l),ansr=cul(r); while(r-l>0.0000001){ // cout<<l<<" "<<r<<endl; double mid=(l+r)/2; double ansm=cul(mid); double lmid=(mid+l)/2; double rmid=(r+mid)/2; double anslm=cul(lmid); double ansrm=cul(rmid); if(anslm<ansm){ r=mid; ansr=ansm; }else{ l=lmid; ansl=anslm; } if(ansrm<ansm){ l=mid; ansl=ansm; }else{ r=rmid; ansr=ansrm; } } return l; } int cas=0; int main(){ //////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ cin>>N; for(int i=0;i<N;i++){ scanf("%lf%lf",&x[i],&w[i]); } // cout<<cul(0)<<endl; printf("Case #%d: %.0f\n",++cas,cul(work())); } return 0; }
题目:求出一个图的哈密顿回路,保证每个点的出度大于点数的一半。
思路:感觉dfs肯定会tle但是大家基本都是dfs水过的,一种可能是数据太水,一种可能是由于出度的限制(这种图的哈密顿回路是一定存在的),dfs一定可以迅速出解,具体什么数据可以卡掉dfs还没想出,可能真的卡不掉吧。。。哈密顿回路的算法是O(n^2)的,并且也需要这个稠密图的条件。
PS:我dfs居然写挂了。。。还没搞清楚究竟挂在哪里了。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=160; bool G[maxv][maxv]; int N,M; bool m[maxv]; bool dfs(int v,int num){ if(num==N){ if(G[v][1]){ return 1; } else return 0; } for(int i=1;i<=N;i++){ if(!m[i]&&G[v][i]){ m[i]=1; if(dfs(i,num+1)){ printf("%d ",i); return 1; } m[i]=0; } } return 0; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ while(cin>>N>>M){ memset(G,0,sizeof G); for(int i=0;i<M;i++){ int x,y; scanf("%d%d",&x,&y); G[x][y]=G[y][x]=1; } memset(m,0,sizeof m); m[1]=1; if(dfs(1,1)){ printf("%d\n",1); }else{ puts("no solution"); } } return 0; }
题目:一个字符串可以做如下变换,取两个字符交换位置并且字符变大一位,现在给出两个字符串,问是否可以由某种变换方式使两个字符串一致。
思路:本来是按照cf的一道题的思路来想的,这个变换满足等价关系的定义,因为任何变换可以在做26次之后返回初始状态,所以是可逆的。然后就是要找到一个标准把两个字符串变成一样的(比如字典序最小),但是直接贪心变换似乎不能直接得到字典序最大或最小的等价串。然后就卡住了。。。其实想想也是当然的,这种题目的重点是如何变换,发现这种变换的某种普遍性质才是解题的关键。。。那么这个题目中的变换有什么特点呢?嗯。。比较棘手的一点是这个字符变换的结果是与顺序有关的,所以不能直接交换字符,这时候就需要YY一下(但是我并没有YY出来。。郁闷。。)。。。首先我们应该容易注意到奇偶性的特点,每次变换字符串整体的和加2,也就是总和的奇偶性不变,并且如果奇数字符有偶数个那么不管怎么变,奇数字符个数的奇偶性是不会变的。这时候一个猜想就是奇数字符个数的奇偶性直接决定了字符串的等价性。(以下是队友的分析),首先奇偶性不同的一定不等价,如果奇偶性相同,那么可以先进行变换使得奇数字符的个数也相同(这时候同时保证奇対奇,偶对偶),然后把最后一位的字符当做中转,把前面的奇偶字符全部换成相等(因为每两次变换,字符的值增加2,所以必能相等),现在剩下作为中转的字符可能不等,那么我们需要一种方法,在不改变其他字符的情况下,把最后两个不等的字符变相等。考虑三个字符abc,做交换cba,bca,cba,abc,这四个交换后字符c的值增加4,而ab我们可以通过24次交换变回原值,由于有26个字符的取模作用,实际上变换7次,c增值28相当于c增值2,而最后两个字符奇偶相同,那么必然可以变成相同,证闭。
关于顺序,对于三个数,通过把一对数交换13次,再把另一对数交换13次,再交换13次使三个数变回原值,可以达到任意互换位置的效果。
详见:http://blog.csdn.net/acdreamers/article/details/9390907
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; int T; string a,b; int cas=0; void change(char &a,char &b){ int a1=a-'a',b1=b-'a'; a1=(a1+1)%26,b1=(b1+1)%26; a=b1+'a',b=a1+'a'; } int main(){ /////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ cin>>T; while(T--){ cin>>a>>b; int len=a.size(); bool f=0; if(len==2){ for(int i=0;i<30;i++){ if(a==b) f=1; change(a[0],a[1]); } }else{ int cnt1=0,cnt2=0; for(int i=0;i<len;i++){ if(a[i]%2) cnt1++; if(b[i]%2) cnt2++; } if(cnt1%2==cnt2%2) f=1; } if(!f) printf("Case #%d: NO\n",++cas); else printf("Case #%d: YES\n",++cas); } return 0; }
题目:问n的组合数中奇数有多少个。
思路:找规律水过,看题解说是lucas定理,没仔细看,暂且记下。
题目:某个树的节点都是2的1到n次幂并且互不相同,问保证所有左子树的节点和大于右子树的树共有多少种。
思路:dp,首先是2的幂,实际上不论节点集合的权值如何,集合中权值最大得点属于哪个子树是已经确定的,然后就是dp的状态,是节点数为n、的时候深度小于等于d的子树有多少种,题目中的节点虽然看似都不同,但是子树的个数与节点值并无关系(就结论而言。。。。)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=370; const ll mod=1e9+7; ll C[maxv][maxv]; ll dp[maxv][maxv]; int N,D; int cas=0; void init(){ memset(C,0,sizeof C); for(int i=0;i<maxv;i++){ C[i][0]=1,C[i][i]=1; } for(int i=1;i<=maxv;i++){ for(int j=1;j<=i;j++){ C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod; } } memset(dp,0,sizeof dp); for(int i=1;i<maxv;i++) dp[1][i]=1; for(int i=2;i<maxv;i++) dp[2][i]=4; for(int i=3;i<maxv;i++){ for(int j=1;j<maxv;j++){ for(int k=1;k<i-1;k++){ dp[i][j]=(dp[i][j]+i*dp[k][j-1]%mod*C[i-2][k]%mod*dp[i-1-k][j-1]%mod)%mod; } dp[i][j]=(dp[i][j]+i*dp[i-1][j-1]*2%mod)%mod; } } } int main(){ ////////freopen("/home/files/CppFiles/in","r",stdin); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ init(); int T; cin>>T; while(T--){ cin>>N>>D; printf("Case #%d: %lld\n",++cas,((dp[N][D]-dp[N][D-1])%mod+mod)%mod); } return 0; }
题目:每个置换都有一个最小循环节,现要求对于长度为n的置换循环节的长度有多少种。
思路:虽然看起来是和置换群有关,但是实际上由于最小循环节等于置换中所有环的最小公倍数,问题就变成了求出和为n的若干正整数的最小公倍数有多少种。又因为任意个数的1不影响结果且不使用质数而使用不同质数组成合数不可能增加最小公倍数的个数,故问题转变为质数和其幂相加小于n的方案有多少种。这是个背包问题,要求统计种数,参考了kuangbin大神的记忆化搜索实现。。。。t了n发结果最后发现是因为质数筛的范围略小。。。幸亏数据范围小可以在本机都试一遍。。。。另外。。dp的初值也想得有点问题。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1300; ll dp[maxv][maxv]; int h=0; int pri[maxv]; bool m[maxv]; int N; void init(){ memset(m,0,sizeof m); for(int i=2;i<maxv;i++){ if(!m[i]){ pri[h++]=i; } for(int j=0;j<h&&i*pri[j]<maxv;j++){ m[i*pri[j]]=1; if(i%pri[j]==0) break; } } memset(dp,-1,sizeof dp); return; } ll dfs(int n,int p){ if(dp[n][p]!=-1) return dp[n][p]; if(pri[p]>n) return dp[n][p]=1; dp[n][p]=0; for(int i=pri[p];i<=n;i*=pri[p]){ dp[n][p]+=dfs(n-i,p+1); } dp[n][p]+=dfs(n,p+1); return dp[n][p]; } int main(){ ///////freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); /* std::ios::sync_with_stdio(false); std::cin.tie(0);*/ init(); while(scanf("%d",&N)!=EOF){ printf("%lld\n",dfs(N,0)); } return 0; }
8 CF 547D(欧拉回路
题目:给出若干个坐标,现在把每个点染成红蓝两色,要求每一行和每一列的红色和蓝色点数相差不超过1.
思路:首先,此题必然有解,如果将每一行和每一列分别作为点构图,每一个点对应一条边的话,染色这个点就对应与染色这条边,而对于图中的环来说,只要交替染色,就能保证环中的点差为0,对于剩下的无环图,交替染色,差最大为1.。然后问题在于如何求解,每次找环然后删的话效率比较低也不好实现。。。cf的官方题解是这样做的:对于出度为奇的点,删去一条临边,让然后递归求解,所有点的度都为偶数的时候,对每一点求欧拉回路,求的时候直接删边,把经过的点存下以便标记。。。对于删掉的边,回溯的时候根据两边的点的度判断染成什么颜色。。。所有的维护奇数偶数度的点,以及边的染色和删边都可以用set和map实现,处理的时候可以lazy操作,在需要用边的时候再删掉标记删掉的边。
ps:想清楚为啥删掉的边只根据一点直接染色?依照这种方法,每次删掉一条链,删完之后链两端的度会差一,由于不会有两条链共端点,所以显然符合要求。
(代码基本是完全照抄cf标程的,自己完全不会实现。。。)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e5+3000; vector<int> G[maxv*2]; set<int> D[2]; map<P,bool> del,mp; bool deg[maxv*2]; vector<P> es; vector<int> eu; int x[maxv*2]; void addedge(int u,int v){ G[v].pb(u); G[u].pb(v); es.pb(P(u,v)); } void eular(int v){ while(!G[v].empty()){ int u=G[v].back(); G[v].PB(); if(!del[P(u,v)]){ del[P(u,v)]=del[P(v,u)]=1; eular(u); } } eu.pb(v); } void deledge(int u,int v){ del[P(u,v)]=del[P(v,u)]=1; D[deg[v]].erase(v); D[deg[u]].erase(u); deg[u]=!deg[u]; deg[v]=!deg[v]; D[deg[u]].insert(u); D[deg[v]].insert(v); } int n; void solve(){ if(!D[1].empty()){ int v=*D[1].begin(); while(!G[v].empty()&&del[P(v,G[v].back())]) G[v].PB(); int u=G[v].back(); deledge(v,u); solve(); int c=0; if(x[u]>0) c=1; if(c==1){ x[u]--,x[v]--; }else{ x[u]++,x[v]++; } mp[P(u,v)]=mp[P(v,u)]=c; }else{ for(int i=0;i<maxv*2;i++){ eu.clear(); eular(i); for(int j=0;j<eu.size()-1;j++){ mp[P(eu[j],eu[j+1])]=mp[P(eu[j+1],eu[j])]=j%2; } } } } int main(){ cin>>n; for(int i=0;i<n;i++){ int x,y; scanf("%d%d",&x,&y); x--,y--; x=2*x+1; y=2*y; addedge(x,y); } for(int i=0;i<2*maxv;i++){ D[G[i].size()%2].insert(i),deg[i]=G[i].size()%2; } solve(); for(int i=0;i<es.size();i++){ printf("%c",mp[es[i]]?'r':'b'); } return 0; }
9 PKU 1986(lca
题目:要求一棵树上任意两点之间的距离。
思路:裸的lca,用的是挑战程序设计竞赛上的倍增的方法,在二分求lca的同时算出距离。也可以使用rmq的方法做。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) #define clr(x) memset((x),0,sizeof (x)) using namespace std; const int maxv=4e4+3000; struct EDGE{ int t,w; EDGE *nt; }pool[maxv*4]; int ph=0; EDGE *newedge(){ return &pool[ph++]; } EDGE *head[maxv],*tail[maxv]; void addedge(int x,int y,int w){ tail[x]->nt=newedge(); tail[x]=tail[x]->nt; tail[x]->nt=NULL; tail[x]->t=y,tail[x]->w=w; } int n,q,s; int par[30][maxv*2],deep[maxv*2],dis[30][maxv*2]; void clear(){ memset(par,-1,sizeof par); memset(dis,-1,sizeof dis); ph=0; for(int i=0;i<maxv;i++){ head[i]=newedge(); head[i]->nt=NULL; tail[i]=head[i]; } } void dfs(int v,int p,int d,int di){ par[0][v]=p; dis[0][v]=di; deep[v]=d; for(EDGE *i=head[v]->nt;i!=NULL;i=i->nt){ if(i->t==p) continue; dfs(i->t,v,d+1,i->w); } } void init(int v){ dfs(1,-1,0,0); for(int k=0;k<25;k++){ for(int v=1;v<=n;v++){ if(par[k][v]<0) par[k+1][v]=-1; else par[k+1][v]=par[k][par[k][v]]; dis[k+1][v]=dis[k][par[k][v]]+dis[k][v]; } } } int lca(int u,int v){ if(deep[u]>deep[v]) swap(u,v); int dd=0; for(int k=0;k<25;k++){ if((deep[v]-deep[u])>>k&1){ dd+=dis[k][v]; v=par[k][v]; } } if(u==v) return dd; for(int k=24;k>=0;k--){ if(par[k][u]!=par[k][v]){ dd+=dis[k][u]+dis[k][v]; u=par[k][u]; v=par[k][v]; } } return dis[0][u]+dis[0][v]+dd; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n>>q){ clear(); for(int i=0;i<n-1;i++){ int x,y,z; char e; scanf("%d%d%d %c",&x,&y,&z,&e); addedge(x,y,z); addedge(y,x,z); } dfs(1,-1,0,0); init(1); int K; cin>>K; while(K--){ int a,b; scanf("%d%d",&a,&b); cout<<lca(a,b)<<endl; } } return 0; }
10 HDU 4372(组合数学,斯特林数
题目:n个建筑物,高度各不相同,如果后面一个的高度大于前一个就能看见,现在从前往后看能看见f个,从后往前看能看见b个,问有多少种可能的排列。
思路:两个建筑之间的建筑物高度比前一个小,那么他们的排列种数就是n-1!,恰好是n的圆排列,选出f+b个环对应第一类斯特林数。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) #define clr(x) memset((x),0,sizeof (x)) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const ll mod=1e9+7; const int maxv=2000+5; ll S[maxv][maxv]; ll C[maxv][maxv]; void init(){ for(int i=0;i<maxv;i++){ C[i][0]=1; if(i!=0) S[i][1]=1; S[i][i]=1; } for(int i=1;i<maxv;i++){ for(int j=1;j<=i;j++){ C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; S[i][j]=((i-1)*S[i-1][j]+S[i-1][j-1])%mod; } } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); init(); int N,F,B; int T; cin>>T; while(T--){ scanf("%d%d%d",&N,&F,&B); ll ans=0; int f=F-1,b=B-1; if(f+b<=N-1) ans=(S[N-1][f+b]*C[f+b][f])%mod; printf("%d\n",(int)ans); } return 0; }
11 CF 567C
题目:给出一个序列和k值,求有多少个三个元素且公比为k的子列。
思路:枚举中间值,然后用两个map维护前后的值,这题比较恶心的是卡ll,卡000的情况。。。wa了5发看了数据才过得。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) #define clr(x) memset((x),0,sizeof (x)) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; map<ll,ll> m1,m2; const int maxv= 2e5+300; ll n,k; ll a[maxv]; int main(){ /// freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>k; for(int i=0;i<n;i++){ cin>>a[i]; if(m2.find(a[i])!=m2.end()){ m2[a[i]]++; }else{ m2[a[i]]=1; } } ll n0=m2[0]; ll ans=n0*(n0-1)*(n0-2)/6; for(int i=0;i<n;i++){ if(m2[a[i]]==1) m2.erase(a[i]); else m2[a[i]]--; if(k==1&&a[i]!=0){ ans+=m1[a[i]]*(m1[a[i]]-1)/2; } else if(a[i]%k==0&&a[i]!=0){ if(m1.find(a[i]/k)!=m1.end()&&m2.find(a[i]*k)!=m2.end()){ ans+=m1[a[i]/k]*m2[a[i]*k]; } } m1[a[i]]++; } cout<<ans<<endl; return 0; }
12 HDU 4370(最短路
题目:给出一个矩阵,要求一个矩阵满足若干条件使结果最小(详见原题
思路:几乎看不出是最短路啊。。。。条件实际上对应1的出度为1,n的入度为1,其他点的出度等于入度。c矩阵对应边权。还有要注意两个环的情况。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=305; int n; int C[maxv][maxv]; ll dis[maxv]; ll dis2[maxv]; priority_queue<P,vector<P>,greater<P> > Q; ll cyc1,cyc2; void dij1(){ memset(dis,0x3f,sizeof dis); dis[1]=0; ll cyc=1e18; Q.push(P(0,1)); while(!Q.empty()){ ll v=Q.top().se,d=Q.top().fs; Q.pop(); if(d>dis[v]) continue; for(int i=1;i<=n;i++){ if(dis[i]>dis[v]+C[v][i]){ dis[i]=dis[v]+C[v][i]; Q.push(P(dis[i],i)); } if(i!=1){ cyc1=min(cyc1,dis[i]+C[i][1]); } } } } void dij2(){ memset(dis2,0x3f,sizeof dis2); dis2[n]=0; ll cyc=1e18; Q.push(P(0,n)); while(!Q.empty()){ ll v=Q.top().se,d=Q.top().fs; Q.pop(); if(d>dis2[v]) continue; for(int i=1;i<=n;i++){ if(dis2[i]>dis2[v]+C[v][i]){ dis2[i]=dis2[v]+C[v][i]; Q.push(P(dis2[i],i)); } if(i!=n){ cyc2=min(cyc2,dis2[i]+C[i][n]); } } } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ scanf("%d",&C[i][j]); } } cyc1=cyc2=1e18; dij1(); dij2(); cout<<min(dis[n],cyc1+cyc2)<<endl; } return 0; }
13 HDU 5294(最短路,最小割
题目:求出s到t的最短路的最小边数,以及让最短路变长所需要删除的最小边数。
思路:前者就是最短路加点东西,后者是在最短路上的边组成的图上求最大流。最短路上的边就是d[u]==d[v]+w的边。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define IINF (1<<29) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e3+400; struct edge{ int to,cap,rev; }; queue<int> que; vector<edge> nG[maxv]; int level[maxv]; int iter[maxv]; void add_edge(int from,int to,int cap){ nG[from].pb((edge){to,cap,nG[to].size()}); nG[to].pb((edge){from,0,nG[from].size()-1}); } void bfs(int s){ memset(level,-1,sizeof level); level[s]=0; que.push(s); while(!que.empty()){ int v=que.front();que.pop(); for(int i=0;i<nG[v].size();i++){ edge &e=nG[v][i]; if(e.cap>0&&level[e.to]<0){ level[e.to]=level[v]+1; que.push(e.to); } } } } int dfs(int v,int t,int f){ if(v==t){ return f; } for(int &i=iter[v];i<nG[v].size();i++){ edge &e=nG[v][i]; if(e.cap>0&&level[v]<level[e.to]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; nG[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){ int flow=0; for(;;){ bfs(s); if(level[t]<0) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0){ flow+=f; } } } struct EDGE{ int t,d; EDGE(int t,int d):t(t),d(d){} }; int n,m; int minn[maxv]; ll dis[maxv]; vector<EDGE> G[maxv]; void clear(){ for(int i=0;i<maxv;i++){ G[i].clear(); nG[i].clear(); } } priority_queue<P,vector<P>,greater<P> > Q; void dij(int s){ memset(minn,0x3f,sizeof minn); memset(dis,0x3f,sizeof dis); dis[s]=0; minn[s]=0; Q.push(P(0,s)); while(!Q.empty()){ ll v=Q.top().se,d=Q.top().fs; Q.pop(); if(d>dis[v]) continue; for(int i=0;i<G[v].size();i++){ int u=G[v][i].t,d=G[v][i].d; if(dis[u]>dis[v]+d){ dis[u]=dis[v]+d; minn[u]=minn[v]+1; Q.push(P(dis[u],u)); }else if(dis[u]==dis[v]+d&&minn[u]>minn[v]+1){ minn[u]=minn[v]+1; Q.push(P(dis[u],u)); } } } } void makeG(){ for(int i=1;i<=n;i++){ for(int j=0;j<G[i].size();j++){ EDGE &e=G[i][j]; if(dis[e.t]==dis[i]+e.d){ add_edge(i,e.t,1); } } } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); while(cin>>n>>m){ clear(); for(int i=0;i<m;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); G[x].pb(EDGE(y,z)); G[y].pb(EDGE(x,z)); } dij(1); makeG(); int maxflow=dinic(1,n); int minnum=minn[n]; cout<<maxflow<<" "<<m-minnum<<endl; } return 0; }
14 HDU 4374(dp,单调队列
题目:是男人就下100层!!每层可以移动t步,问经过格子的最大和是多少。
思路:利用前缀和再稍加变形可以发现每次的dp值实际上是滑窗上的最值,故可用单调队列处理。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) #define clr(x) memset((x),0,sizeof (x)) typedef long long ll; //Ŕ#define int ll typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxn=105; const int maxm=1e4+40; int N,M,X,T; int s[maxn][maxm]; ll dp[maxn][maxm]; ll sum[maxn][maxm]; P q1[maxm],q2[maxm]; int q1f,q1b,q2f,q2b; void add(P *q,int &f,int &b,ll v,int o){ if(b-f==0){ q[b++]=P(v,o); return; } while(b-f>0&&q[b-1].fs<v){ b--; } q[b++]=P(v,o); return; } ll getf(P *q,int &f,int &b,int o){ while(b-f>0&&o-q[f].se>T){ f++; } return q[f].fs; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>N>>M>>X>>T){ for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ scanf("%d",&s[i][j]); sum[i][j]=sum[i][j-1]+s[i][j]; } } for(int i=1;i<=M;i++){ dp[1][i]=-1e15; } dp[1][X]=0; for(int i=2;i<=N+1;i++){ q1f=q1b=q2f=q2b=0; for(int j=1;j<=min(T,M);j++){ ll v=dp[i-1][j]+sum[i-1][j]; add(q2,q2f,q2b,v,j); } for(int j=1;j<=M+1;j++){ if(j+T<=M){ ll v=dp[i-1][j+T]+sum[i-1][j+T]; add(q2,q2f,q2b,v,j+T); } ll v=dp[i-1][j]-sum[i-1][j-1]; if(j<=M) add(q1,q1f,q1b,v,j); dp[i][j]=max(getf(q1,q1f,q1b,j)+sum[i-1][j],getf(q2,q2f,q2b,j+T)-sum[i-1][j-1]); } } ll ans=-1e18; for(int i=1;i<=M;i++){ ans=max(ans,dp[N+1][i]); } cout<<ans<<endl; } return 0; }
15 HDU 2256(矩阵快速幂
题目:http://acm.hdu.edu.cn/showproblem.php?pid=2256
思路:之前做过一道差不多的,今天竟然又不会。。。看来还是要多复习啊。。。。。参见这篇题解
http://www.cnblogs.com/wally/archive/2013/03/01/2939318.html
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define IINF (1<<29) using namespace std; typedef long long ll; typedef pair<ll,ll> P; const ll mod=1024; const int sz=2; struct matrix{ ll m[20][20]; matrix(){ memset(m,0,sizeof m); } void mul(matrix b){ matrix c; for(int i=0;i<sz;i++){ for(int j=0;j<sz;j++){ c.m[i][j]=0; for(int k=0;k<sz;k++){ c.m[i][j]=(c.m[i][j]+m[i][k]*b.m[k][j])%mod; } } } for(int i=0;i<sz;i++){ for(int j=0;j<sz;j++){ m[i][j]=c.m[i][j]; } } } }; matrix qpow(matrix x,ll p){ matrix ans; for(int i=0;i<sz;i++){ ans.m[i][i]=1; } while(p>0){ if(p&1) ans.mul(x); x.mul(x); p>>=1; } return ans; } int T; int main(){ freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); matrix cons; cons.m[0][0]=5,cons.m[0][1]=12,cons.m[1][0]=2,cons.m[1][1]=5; ll n; cin>>T; while(T--){ cin>>n; matrix ans=qpow(cons,n-1); cout<<((ans.m[0][0]*5+ans.m[0][1]*2)*2-1+mod)%mod<<endl; } return 0; }
16 CF Gym 100625B(概率dp
题目:需要贿赂n个人,每个人需要的钱和贿赂成功的概率给出,问最后能贿赂到c个人的最大概率是多少。
思路:自己思路不清楚。。。开始想的是枚举这些钱能贿赂的人的集合,然后计算每个方案的成功概率,然而。。。枚举集合就是很高的复杂度,计算成功概率还的用容斥原理。。。简直爆炸。。。。后来发现是理解错题意了,此题有个很关键的地方是“每次贿赂完一个人后你可以立即知道是否成功,再贿赂下一个人”,显然,贿赂了足够多的人之后就不用再贿赂,所以这题的关键是顺序,故dfs的时候枚举的是“在当前可选的人中第一个选的是谁”,根据这个进行转移,同时,对于已经选过的集合,顺序无关(因为成功的还没到需要的数量),所以可以进行dp。状态是已经贿赂过的集合,还剩多少需要成功贿赂的人。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<30) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxv=16; int T,n,c,m; int b[maxv],p[maxv]; double dp[1<<maxv][20]; int num[1<<maxv]; int cost[1<<maxv]; void clear(){ for(int i=0;i<(1<<maxv);i++){ for(int j=0;j<20;j++){ dp[i][j]=-1; } } } double dfs(int mask,int y){ if(dp[mask][y]!=-1) return dp[mask][y]; if(cost[mask]>m) return dp[mask][y]=0; if(y==0) return 1; dp[mask][y]=0; for(int i=0;i<n;i++){ if(mask&(1<<i)) continue; dp[mask][y]=max(dp[mask][y],dfs(mask|(1<<i),y-1)*p[i]/100.0+dfs(mask|(1<<i),y)*(100-p[i])/100.0); } return dp[mask][y]; } void cul(int mask,int nu,int co,int p){ if(p==n){ num[mask]=nu; cost[mask]=co; return; } cul(mask^(1<<p),nu+1,co+b[p],p+1); cul(mask,nu,co,p+1); } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ clear(); cin>>n>>c>>m; for(int i=0;i<n;i++){ scanf("%d%d",b+i,p+i); } cul(0,0,0,0); cout<<dfs(0,c)<<endl; } return 0; }
17 CF 568B
题目:求元素数量为n的关系中,具有传递,对称,但是不自反的关系的数目。
思路;传递,对称且自反的关系是等价关系,满足前两者但不满足自反的关系只能是某些元素不存在这个关系集合中(如果它与自身相关,矛盾,如果某一其他元素相关,那么根据对称和传递性,仍然自反),故可以求出m个元素的等价关系的数量,再加入n-m个空元素,,等价关系的数量即bell数,可由stirling数计算。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const ll mod=1e9+7; const int maxv=4005; ll S[maxv][maxv]; ll B[maxv]; ll C[maxv][maxv]; void init(){ for(int i=1;i<maxv;i++){ S[i][i]=1; } B[0]=B[1]=1; for(int i=2;i<maxv;i++){ for(int j=1;j<maxv;j++){ S[i][j]=(S[i-1][j-1]+S[i-1][j]*j%mod)%mod; B[i]=(B[i]+S[i][j])%mod; } } for(int i=0;i<maxv;i++){ C[i][i]=1; C[i][0]=1; } for(int i=1;i<maxv;i++){ for(int j=1;j<maxv;j++){ C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } } } int main(){ // freopen("/home/files/CppFiles/in","r",stdin); init(); int n; while(cin>>n){ ll ans=0; for(int i=0;i<n;i++){ ans=(ans+B[i]*C[n][i])%mod; } cout<<ans<<endl; } return 0; }
18 HDU3068(裸manacher算法
题目:最长回文连续串。
思路:http://blog.csdn.net/ggggiqnypgjg/article/details/6645824/ 裸算法,参考此文
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e5; char read[maxv]; char s[maxv*2]; int p[maxv]; int len; int h; void manacher(){ p[1]=1; int mx=0; int mxid=1; for(int i=1;i<h-1;i++){ if(mx>i){ p[i]=min(p[mxid*2-i],mx-i); }else{ p[i]=1; } for(;s[i+p[i]]==s[i-p[i]];p[i]++); if(p[i]+i>mx){ mx=p[i]+i; mxid=i; } } } int main(){ ///freopen("/home/files/CppFiles/in","r",stdin); while(scanf("%s",read)!=EOF){ len=strlen(read); s[0]='$'; h=1; for(int i=0;i<len;i++){ s[h++]='#'; s[h++]=read[i]; } s[h++]='#'; s[h++]='&'; manacher(); int ans=0; for(int i=1;i<h-1;i++){ ans=max(ans,p[i]-1); } printf("%d\n",ans); } return 0; }
19 HDU 5371(回文串,set
题目:给出一个数列,要求求出满足类似12332123这种关系的子串的最大长度。
思路:先利用manacher算法预处理出以每个数字为中心的回文串的最大长度,然后按照回文长度排序,利用set更新ans。
参考多校题解:http://bestcoder.hdu.edu.cn/blog/2015-multi-university-training-contest-7-solutions-by-uestc/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=1e5+3000; int a[maxv*2]; int p[maxv*2]; P o[maxv*2]; int T,n; void manacher(){ int mx=0; int mxid; int i; for(int i=1;i<2*n+2;i++){ if(mx>i){ p[i]=min(p[mxid*2-i],mx-i); }else{ p[i]=1; } for(;a[i-p[i]]==a[i+p[i]];p[i]++); if(mx<p[i]+i){ mx=p[i]+i; mxid=i; } if(i%2==1){ o[i/2]=P(p[i],i/2); } } } set<int> S; int cas=0; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ S.clear(); cin>>n; a[0]=-1; for(int i=1;i<=n;i++){ scanf("%d",a+i*2); a[2*i-1]=-2; } a[2*n+1]=-2; a[2*n+2]=-3; manacher(); sort(o+1,o+n+1,greater<P>() ); int ans=0; for(int i=1;i<=n;i++){ int l=o[i].fs-1; int s=o[i].se; set<int>::iterator it=S.upper_bound(s+l/2); if(it!=S.begin()){ it--; ans=max(ans,*it-s); } it=S.lower_bound(s-l/2); if(it!=S.end()) ans=max(ans,s-*it); S.insert(s); } printf("Case #%d: %d\n",++cas,ans*3); } return 0; }
20 HDU 5372(树状数组
题目:每次添加或删除一条线段,询问某区间内的线段条数。
思路:这题有个关键条件是每次添加的线段长度+1,这样的话不会出现先添加的线段包括后面线段的情况,所以只需要两个树状数组维护起止端点即可。没想到简直是智商太低。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define pb push_back #define se second #define fs first #define sq(x) (x)*(x) #define eps 0.000000001 using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxv=2e5+300; struct BIT{ ll a[maxv*3]; void add(int p,int x){ while(p<maxv*3){ a[p]+=x; p+=p&-p; } } int sum(int p){ int ans=0; while(p>0){ ans+=a[p]; p-=p&-p; } return ans; } void clear(){ memset(a,0,sizeof a); } }A,B; int n; int h,add,tol; int opa[maxv]; P op[maxv]; ll disc[maxv*3]; int cas=0; int idx(int x){ return lower_bound(disc,disc+tol,x)-disc+1; } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n){ int add=1; A.clear(),B.clear(); h=0; add=1; printf("Case #%d:\n",++cas); for(int i=0;i<n;i++){ int o,b; scanf("%d%d",&o,&b); op[i].fs=o,op[i].se=b; if(o==0){ disc[h++]=op[i].se; disc[h++]=op[i].se+add; add++; } } sort(disc,disc+h); add=1; tol=unique(disc,disc+h)-disc; for(int i=0;i<n;i++){ if(op[i].fs==0){ int x=idx(op[i].se),y=idx(op[i].se+add); printf("%d\n",-A.sum(x-1)+B.sum(y)); opa[add]=op[i].se; A.add(x,1); B.add(y,1); add++; }else{ int x=idx(opa[op[i].se]),y=idx(opa[op[i].se]+op[i].se); A.add(x,-1); B.add(y,-1); } } } return 0; }
21 HDU 5379(树形dp
题目:给一个树的每个节点赋值,使得每个节点的子节点标号连续,每个节点的所有后代节点的标号也连续,标号1~n,问有多少种赋值方法。
思路:预处理出每个节点的后代节点,叶子节点,非叶子节点个数,dp处理每棵子树的方案数。
参考多校题解:http://bestcoder.hdu.edu.cn/blog/2015-multi-university-training-contest-7-solutions-by-uestc/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<30) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const ll mod=1e9+7; const int maxv=1e5+400; int n; vector<int> G[maxv]; int tsonnum[maxv]; int sonnum[maxv]; int dsonnum[maxv]; ll f[maxv]; int T; void init(){ f[0]=1; for(int i=1;i<maxv;i++){ f[i]=(f[i-1]*i)%mod; } } void clear(){ for(int i=0;i<maxv;i++){ G[i].clear(); tsonnum[i]=0; sonnum[i]=0; dsonnum[i]=0; } sonnum[maxv-1]=1; } void cont(int v,int fa){ tsonnum[v]=1; for(int i=0;i<G[v].size();i++){ int u=G[v][i]; if(u==fa) continue; cont(u,v); sonnum[v]++; tsonnum[v]+=tsonnum[u]; if(tsonnum[u]>1){ dsonnum[v]++; } } } ll dfs(int v,int fa){ if(dsonnum[v]>2){ return 0; } ll ans=1; for(int i=0;i<G[v].size();i++){ int u=G[v][i]; if(u==fa) continue; ans=(ans*dfs(u,v))%mod; } ans=(ans*f[sonnum[v]-dsonnum[v]])%mod; if(dsonnum[v]<=1&&tsonnum[v]>1) ans=(ans*2)%mod; return ans; } int cas=0; int main(){ freopen("/home/files/CppFiles/in","r",stdin); init(); cin>>T; while(T--){ cin>>n; clear(); for(int i=0;i<n-1;i++){ int x,y; scanf("%d%d",&x,&y); G[x].pb(y); G[y].pb(x); } cont(1,-1); printf("Case #%d: %lld\n",++cas,dfs(1,-1)); } return 0; }
22 CF Gym 100379H(博弈问题
题目:两个人轮流取石子,每次最多可取logn个,无法操作就输,问n个石子时第一个是必胜还是必败。
思路:石子数在2^k-1~2^(k+1)之间的必败态间隔为k,由此可以递推出必败态的位置。注意有个坑点是如果+k后正好是2的幂,那么这个不是必败态,因为此时可以加的数变成了k+1.递推必败态的方式很巧妙。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.0000000001 #define IINF (1<<30) using namespace std; typedef long long ll; typedef pair<ll,ll> P; ll n; int T; int main(){ // freopen("/home/files/CppFiles/in","r",stdin); //freopen("/home/files/CppFiles/out","w",stdout); cin>>T; while(T--){ cin>>n; if(n==1){ puts("2"); continue; }else if(n==2){ puts("1 1"); continue; } ll k=4,b=2,s=3; while((k<<1)<=n){ k<<=1; s+=(k-s)/(b+1)*(b+1); if(s==k) s-=b+1; b++; } if((n-s)%(b+1)==0){ printf("%lld\n",2LL); }else{ ll add=(n-s)/(b+1); add=s+add*(b+1); printf("%lld %lld\n",1LL,n-add); } } return 0; }
23 CF Gym 100379G(博弈问题
题目:三堆石子,两个人轮流操作,可以拿走一堆里的任意数量或者三堆都拿走同样数量。问谁能赢。
思路:Nim游戏的变种,实际上仍然是简单的异或就能搞定,异或是0为必败态,理由如下:对于必胜态,一定可以取一堆石子中的若干使得nim值为0,对于必败态,不论如何取(如果三堆都取,那么取后nim值不为0,“如果为0,那么取之前的nim值就不能为0,因为是3堆,矛盾”,如果只取一堆,显然)。这样必胜和必败态的转移就与nim游戏一样了。
24 POJ 1274(二分匹配
裸题,好久没写了,贴个代码。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* *@author: Cwind *http://www.cnblogs.com/Cw-trip/ */ #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<29) #define LINF (1ll<<59) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxn=205; vector<int> G[maxn*4]; int n,m; int match[maxn*4]; bool used[maxn*4]; inline void addedge(int a,int b){ G[a].pb(b); G[b].pb(a); } bool dfs(int v){ used[v]=1; for(int i=0;i<G[v].size();i++){ int u=G[v][i],w=match[u]; if(w<0||!used[w]&&dfs(w)){ match[v]=u; match[u]=v; return 1; } } return 0; } inline int bi_match(){ int res=0; memset(match,-1,sizeof match); for(int i=1;i<=200+n;i++){ if(match[i]==-1){ memset(used,0,sizeof used); if(dfs(i)){ res++; } } } return res; } inline void clear(){ for(int i=0;i<maxn*4;i++){ G[i].clear(); } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n>>m){ clear(); for(int i=1;i<=n;i++){ int ss; scanf("%d",&ss); for(int j=0;j<ss;j++){ int k; scanf("%d",&k); addedge(i,k+200); } } cout<<bi_match()<<endl; } return 0; }
25 HDU 1542(矩形面积并,线段树
裸题,用线段树维护当前扫描线扫过的矩形部分总长度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<29) #define LINF (1ll<<59) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxn=105; struct EDGE{ double l,r,h; int u; EDGE(double ll,double rr,double hh,int uu):l(ll),r(rr),h(hh),u(uu){} }; bool cmp(const EDGE &a,const EDGE &b){return a.h<b.h;} int n; double decx[maxn*2]; vector<EDGE> es; inline void add(double x1,double y1,double x2,double y2){ es.pb(EDGE(x1,x2,y2,-1)); es.pb(EDGE(x1,x2,y1,1)); } struct Node { int l,r; double vl,vr; double len,val; int num; Node *ch[2]; }pool[maxn*10]; int ph=0; Node *newNode(){ Node *n=&pool[ph++]; n->val=n->num=0; return n; } Node *root; void build(Node *n,int l,int r){ n->l=l,n->r=r; if(r-l<=1){ n->vr=decx[r]; n->vl=decx[l]; n->len=n->vr-n->vl; return; } n->ch[0]=newNode(); n->ch[1]=newNode(); build(n->ch[0],l,(l+r)/2); build(n->ch[1],(l+r)/2,r); n->len=n->ch[1]->vr-n->ch[0]->vl; n->vr=n->ch[1]->vr,n->vl=n->ch[0]->vl; } void insert(Node *n,int a,int b,int u){ int l=n->l,r=n->r; if(l>=b||a>=r) return; Node *chl=n->ch[0],*chr=n->ch[1]; if(a<=l&&b>=r){ n->num+=u; if(n->num==0){ if(r-l>1) n->val=chl->val+chr->val; else n->val=0; } if(n->num==1&&u==1){ n->val=n->len; } return; } insert(chl,a,b,u); insert(chr,a,b,u); if(!n->num) n->val=chl->val+chr->val; } inline double query(){ return root->val; } inline void init(){ ph=0; root=newNode(); es.clear(); } int xn; #define idx(x) (lower_bound(decx,decx+xn,(x))-decx) int cas=0; int main(){ freopen("/home/files/CppFiles/in","r",stdin); while(cin>>n,n){ init(); double x1,y1,x2,y2; for(int i=0;i<n;i++){ scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); add(x1,y1,x2,y2); decx[2*i]=x1,decx[2*i+1]=x2; } sort(es.begin(),es.end(),cmp); sort(decx,decx+2*n); int xn=unique(decx,decx+2*n)-decx; build(root,0,xn-1); double fore=0,forh=0; double ans=0; for(int i=0;i<es.size();i++){ EDGE &e=es[i]; ans+=(fore)*(e.h-forh); insert(root,idx(e.l),idx(e.r),e.u); forh=e.h; fore=query(); } printf("Test case #%d\n",++cas); printf("Total explored area: %.2f\n\n",ans); } return 0; }
26 PKU1442 (查询区间第k大,treap裸题
大白书模板
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<29) #define LINF (1ll<<59) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; const int maxn=30030; struct Node{ Node *ch[2]; int right,val; int sz; Node():sz(0){} int cmp(int x) const { if(x==val) return -1; return x>val; } void maintain(){ sz=ch[0]->sz+ch[1]->sz+1; } }pool[maxn*10]; int ph=0; Node *null=new Node(); Node *newNode(int v){ Node *n=&pool[ph++]; n->val=v; n->right=rand(); n->ch[0]=n->ch[1]=null; return n; } void rotate(Node *&o,int d){ Node *k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; o->maintain(); k->maintain(); o=k; } void insert(Node *&o,int x){ if(o==null) o=newNode(x); else{ int d=o->val<x; insert(o->ch[d],x); if(o->ch[d]->right>o->right) rotate(o,d^1); } o->maintain(); } void remove(Node *&o,int x){ int d=o->cmp(x); if(d==-1){ if(o->ch[0]==null) o=o->ch[1]; else if(o->ch[1]==null) o=o->ch[0]; else{ int d2=o->ch[0]->right>o->ch[1]->right; rotate(o,d2); remove(o->ch[d2],x); } }else remove(o->ch[d],x); o->maintain(); } int find(Node *o,int x){ while(o!=null){ int d=o->cmp(x); if(d==-1) return 1; else o=o->ch[d]; } return 0; } int kth(Node *o,int k){ if(k==o->ch[0]->sz+1) return o->val; if(o->ch[0]->sz>=k){ return kth(o->ch[0],k); } else{ return kth(o->ch[1],k-o->ch[0]->sz-1); } } int m,n; int a[maxn],u[maxn]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>m>>n; for(int i=1;i<=m;i++){ scanf("%d",a+i); } for(int i=1;i<=n;i++){ scanf("%d",u+i); } int qh=1; Node *root=null; for(int i=1;i<=m;i++){ insert(root,a[i]); while(qh<=n&&u[qh]==i){ printf("%d\n",kth(root,qh)); qh++; } } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* *@author: Cwind *http://www.cnblogs.com/Cw-trip/ */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> #define pb push_back #define PB pop_back #define bk back() #define fs first #define se second #define sq(x) (x)*(x) const int IINF =(1<<29); const double eps=1e-10; using namespace std; typedef long long ll; //typedef pair<ll,ll> P; const int maxn=300; int N,M; int E[maxn][maxn]; int dis[maxn][maxn]; int G[maxn][maxn]; int X[maxn],Y[maxn],B[maxn]; int P[maxn],Q[maxn],C[maxn]; int prev[maxn][maxn]; bool used[maxn]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); scanf("%d%d",&N,&M); for(int i=1;i<=N;i++){ scanf("%d%d%d",&X[i],&Y[i],&B[i]); } for(int i=1;i<=M;i++){ scanf("%d%d%d",&P[i],&Q[i],&C[i]); } for(int i=1;i<=N;i++){ for(int j=1;j<=M;j++){ scanf("%d",&E[i][j]); } } for(int i=0;i<maxn;i++){ for(int j=0;j<maxn;j++){ G[i][j]=IINF; } } for(int j=1;j<=M;j++){ int sum=0; for(int i=1;i<=N;i++){ int dd=abs(X[i]-P[j])+abs(Y[i]-Q[j])+1; sum+=E[i][j]; G[i][j+N]=dd; if(E[i][j]) G[j+N][i]=-dd; } if(sum>0){ G[M+N+1][j+N]=0; } if(sum<C[j]){ G[j+N][M+N+1]=0; } } for(int i=1;i<=N+M+1;i++){ for(int j=1;j<=N+M+1;j++){ prev[i][j]=i; } } for(int k=1;k<=M+N+1;k++){ for(int i=1;i<=M+N+1;i++){ for(int j=1;j<=N+M+1;j++){ if(G[i][j]>G[i][k]+G[k][j]){ G[i][j]=G[i][k]+G[k][j]; prev[i][j]=prev[k][j]; if(i==j&&G[i][j]<0){ memset(used,0,sizeof used); for(int v=i;!used[v];v=prev[i][v]){ used[v]=1; if(v!=N+M+1&&prev[i][v]!=N+M+1){ if(v>N){ E[prev[i][v]][v-N]++; }else{ E[v][prev[i][v]-N]--; } } } puts("SUBOPTIMAL"); for(int x=1;x<=N;x++){ for(int y=1;y<=M;y++){ printf("%d%c",E[x][y],y==M?'\n':' '); } } return 0; } } } } } puts("OPTIMAL"); return 0; }
题目:烙煎饼,每个煎饼要烙两面,但是每个煎饼有个特定的时间段内才能烙,而锅里每次只能同时烙最多k张煎饼,问是否能烙完所有煎饼。
思路:源点到每张煎饼连一条容量为2的边,每个煎饼向可行的时间连1的边,每个时间向汇点连容量k的边,求最大流即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #define pb push_back #define fs first #define se second #define bk back() using namespace std; typedef long long ll; typedef pair<ll,ll> P; /////////////////////////////////////////////// const int IINF=(1<<29); const int MAXV=100000; struct EDGE{ int to,cap,rev; EDGE(int t,int c,int r):to(t),cap(c),rev(r){} }; vector<EDGE> G[MAXV]; void addedge(int from,int to,int cap){///加边 G[from].pb(EDGE(to,cap,G[to].size())); G[to].pb(EDGE(from,0,G[from].size()-1)); } int level[MAXV]; queue<int> Q; void bfs(int s){////bfs出分层图 memset(level,-1,sizeof level); level[s]=0; Q.push(s); while(!Q.empty()){ int v=Q.front();Q.pop(); for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]==-1){ level[e.to]=level[v]+1; Q.push(e.to); } } } } int iter[MAXV]; int dfs(int v,int t,int f){///dfs寻找增广路径 if(v==t) return f; for(int &i=iter[v];i<G[v].size();i++){ EDGE &e=G[v][i]; if(e.cap>0&&level[e.to]>level[v]){ int d=dfs(e.to,t,min(f,e.cap)); if(d>0){ e.cap-=d; G[e.to][e.rev].cap+=d; return d; } } } return 0; } int dinic(int s,int t){///dinic算法求解最大流 int flow=0; for(;;){ bfs(s); if(level[t]==-1) return flow; memset(iter,0,sizeof iter); int f; while((f=dfs(s,t,IINF))>0) flow+=f; } return 0; } int s=MAXV-1; int t=MAXV-2; ///////////////////////////////////////////////////////// const int maxn=10000; int n,k; int ss[maxn],tt[maxn]; int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>n>>k; for(int i=1;i<=n;i++){ cin>>tt[i]>>ss[i]; for(int j=tt[i]+1;j<=tt[i]+ss[i];j++){ addedge(i,j+n,1); } } for(int i=1;i<=n;i++){ addedge(s,i,2); } for(int i=n+1;i<=n+2000;i++){ addedge(i,t,k); } int flow=dinic(s,t); if(flow==n*2){ puts("Yes"); for(int i=1;i<=n;i++){ for(int j=0;j<G[i].size();j++){ if(G[i][j].cap==0){ printf("%d ",G[i][j].to-n-1); } } puts(""); } }else{ puts("No"); } return 0; }
29 Gym 100338C (最短路,求割边。
题目:求删掉一条路边使得最短路变长的边,有重边。
思路:本来思路是很简单的,求出最短路上的边建图,然后求桥就好。但是题目强调了有重边,结果就想着要处理重边,然后wa了一整天。其实这题重边完全不用管,(当然处理掉也可以,不知为啥我一直wa。。)。tarjan算法求桥的时候,dfs只要判断一下不要经过之前经过的边就好了,正好也方便了输出边的编号。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #define pb push_back #define fs first #define se second #define bk back() using namespace std; typedef long long ll; typedef pair<ll,ll> P; const int maxn=20030; const int maxm=1e5+300; struct EDGE{ int to,d,next,id; }es1[maxm*2]; int eh1=0; int head1[maxn]; void addedge1(int from,int to,int d,int id){ es1[eh1].to=to,es1[eh1].d=d;es1[eh1].id=id; es1[eh1].next=head1[from]; head1[from]=eh1++; } ll dis1[maxn],dis2[maxn]; void dij(int s,ll *dis){ priority_queue<P,vector<P>, greater<P> > Q; memset(dis,0x2f,sizeof dis1); dis[s]=0; Q.push(P(0,s)); while(!Q.empty()){ P now=Q.top();Q.pop(); if(dis[now.se]<now.fs) continue; for(int i=head1[now.se];i!=-1;i=es1[i].next){ EDGE &e=es1[i]; if(dis[e.to]>dis[now.se]+e.d){ dis[e.to]=dis[now.se]+e.d; Q.push(P(dis[e.to],e.to)); } } } } struct any{ int len,num,id; any(int len,int num,int id):len(len),num(num),id(id){} any(){} }; map<P,any> M; map<P,any> ::iterator it1,it2; int n,m; vector<P> G[maxn]; int od[maxn]; int low[maxn]; int clo=0; vector<P> ce;//割边 void addedge2(int from,int to,int id){ G[from].pb(P(to,id)); G[to].pb(P(from,id)); } ll mindis; void rebuild(){ for(int i=1;i<=n;i++){ for(int j=head1[i];j!=-1;j=es1[j].next){ EDGE &e=es1[j]; if(dis1[i]+dis2[e.to]+e.d==mindis){ addedge2(i,e.to,e.id); addedge2(e.to,i,e.id); } } } } vector<int> ans; void dfs(int v,int f){ low[v]=od[v]=++clo; for(int i=0;i<G[v].size();i++){ int u=G[v][i].fs; if(G[v][i].se==f) continue; if(!od[u]){ dfs(u,G[v][i].se); low[v]=min(low[v],low[u]); if(low[u]>od[v]){ ans.pb(G[v][i].se); } } else low[v]=min(low[v],od[u]); } } map<P,int > id; int main(){ freopen("/home/files/CppFiles/in","r",stdin); //freopen("important.in","r",stdin); //freopen("important.out ","w",stdout); memset(head1,-1,sizeof head1); cin>>n>>m; for(int i=1;i<=m;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); addedge1(a,b,c,i); addedge1(b,a,c,i); } dij(1,dis1); dij(n,dis2); mindis=dis1[n]; rebuild(); dfs(1,-1); for(int i=0;i<ce.size();i++){ ans.pb(id[ce[i]]); } sort(ans.begin(),ans.end()); ans.erase(unique(ans.begin(),ans.end()),ans.end()); cout<<ans.size()<<endl; for(int i=0;i<ans.size();i++){ printf("%d ",ans[i]); } return 0; }
30 POJ 3463(求最短路和次短路的条数
思路:dijkstra的基本思想,每次取出最小的未扩展的节点扩展,此时此节点的最短路不可能再增长(也不可能有长度一样的其他新路径,因为边长为正),所以标记此节点并扩展,由于要求最短路和次短路,实际上要做2×n次扩展。选择最小的点时可以用优先队列,也可以直接遍历,因为此题数据比较小。
注意一般的dijkstra算法是不用标记vis的,因为不会出现重复入队的情况,但是此题不标记的话区分不了最短路和次短路,会爆内存。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<29) #define LINF (1ll<<59) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; struct EDGE{ int to,d; EDGE(int to,int d):to(to),d(d){} }; const int maxn=1030; vector<EDGE> G[maxn]; int N,M; int S,F; int dis[maxn][2]; int cnt[maxn][2]; bool vis[maxn][2]; const int inf=1e9+300000; void work(){ memset(dis,0x3f,sizeof dis); memset(cnt,0,sizeof cnt); memset(vis,0,sizeof vis); dis[S][0]=0; cnt[S][0]=1; for(;;){ bool find=0; bool witch; int v; int mindis=inf; for(int i=1;i<=N;i++){ if(!vis[i][0]&&dis[i][0]<mindis){ find=1; mindis=dis[i][0]; witch=0; v=i; }else if(!vis[i][1]&&dis[i][1]<mindis){ find=1; mindis=dis[i][1]; witch=1; v=i; } } if(!find) break; vis[v][witch]=1; for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(dis[e.to][0]>dis[v][witch]+e.d){ swap(dis[e.to][0],dis[e.to][1]); swap(cnt[e.to][0],cnt[e.to][1]); dis[e.to][0]=dis[v][witch]+e.d; cnt[e.to][0]=cnt[v][witch]; }else if(dis[e.to][0]==dis[v][witch]+e.d){ cnt[e.to][0]+=cnt[v][witch]; }else if(dis[e.to][1]>dis[v][witch]+e.d){ dis[e.to][1]=dis[v][witch]+e.d; cnt[e.to][1]=cnt[v][witch]; }else if(dis[e.to][1]==dis[v][witch]+e.d){ cnt[e.to][1]+=cnt[v][witch]; } } } } int T; void clear(){ for(int i=0;i<maxn;i++){ G[i].clear(); } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ clear(); cin>>N>>M; for(int i=0;i<M;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); G[a].pb(EDGE(b,c)); } cin>>S>>F; work(); if(dis[F][1]-1==dis[F][0]){ cout<<cnt[F][1]+cnt[F][0]<<endl; }else{ cout<<cnt[F][0]<<endl; } } return 0; }
优先队列版:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #include <vector> #include <queue> #include <stack> #include <functional> #include <set> #include <cmath> using namespace std; #define IOS std::ios::sync_with_stdio(false);std::cin.tie(0) #define pb push_back #define PB pop_back #define fs first #define se second #define sq(x) (x)*(x) #define eps 0.00000001 #define IINF (1<<29) #define LINF (1ll<<59) typedef long long ll; typedef pair<int,int> pii; typedef pair<ll,ll> P; struct EDGE{ int to,d; EDGE(int to,int d):to(to),d(d){} }; const int maxn=1030; vector<EDGE> G[maxn]; struct any{ int v,d,witch; any(int v,int d,int witch):v(v),d(d),witch(witch){} bool operator < (const any &C)const { return d>C.d; } }; int N,M; int S,F; int dis[maxn][2]; int cnt[maxn][2]; bool vis[maxn][2]; const int inf=1e9+300000; void work(){ memset(dis,0x3f,sizeof dis); memset(cnt,0,sizeof cnt); memset(vis,0,sizeof vis); dis[S][0]=0; cnt[S][0]=1; priority_queue<any> Q; Q.push(any(S,0,0)); while(!Q.empty()){ bool witch=Q.top().witch; int v=Q.top().v; int mindis=Q.top().d; Q.pop(); if(vis[v][witch]||dis[v][witch]<mindis) continue; vis[v][witch]=1; for(int i=0;i<G[v].size();i++){ EDGE &e=G[v][i]; if(dis[e.to][0]>dis[v][witch]+e.d){ swap(dis[e.to][0],dis[e.to][1]); swap(cnt[e.to][0],cnt[e.to][1]); dis[e.to][0]=dis[v][witch]+e.d; cnt[e.to][0]=cnt[v][witch]; Q.push(any(e.to,dis[e.to][0],0)); Q.push(any(e.to,dis[e.to][1],1)); }else if(dis[e.to][0]==dis[v][witch]+e.d){ cnt[e.to][0]+=cnt[v][witch]; }else if(dis[e.to][1]>dis[v][witch]+e.d){ dis[e.to][1]=dis[v][witch]+e.d; cnt[e.to][1]=cnt[v][witch]; Q.push(any(e.to,dis[e.to][1],1)); }else if(dis[e.to][1]==dis[v][witch]+e.d){ cnt[e.to][1]+=cnt[v][witch]; } } } } int T; void clear(){ for(int i=0;i<maxn;i++){ G[i].clear(); } } int main(){ freopen("/home/files/CppFiles/in","r",stdin); cin>>T; while(T--){ clear(); cin>>N>>M; for(int i=0;i<M;i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); G[a].pb(EDGE(b,c)); } cin>>S>>F; work(); if(dis[F][1]-1==dis[F][0]){ cout<<cnt[F][1]+cnt[F][0]<<endl; }else{ cout<<cnt[F][0]<<endl; } } return 0; }
需要注意的一点是:不论是最短路还是次短路,当某点的路径长度更新时就要入队。一开始写没有在第一种情况(新路短于最短路)把次短路也入队,结果wa了。(更新路径长度就要入队!!)
31 CodeForces571A(数学题
题目:给三个数abc,还有l,可以给abc增加总共不超过l的长度,问可以组成三角形的不同组合有多少种。
思路:比较考验数学思维的一道题,比赛的的时候一直想不清楚(虽然枚举最长边然后减去不可行的方案是很容易想到的)。实际上对于确定的最长边,不可行的组合就是所有使得b+c的长度小于等于a的 长度的组合(并且这种算法不会重复)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * @author: Cwind * http://www.cnblogs.com/Cw-trip/ */ import java.util.*; import java.util.concurrent.PriorityBlockingQueue; import java.awt.print.Printable; import java.io.*; import java.math.*; public class Main { public static void main(String[] args) throws Exception { Scanner in = new Scanner(new File( "/home/develop/eclipse_file/ACMproject/src/in")); // PrintWriter out=new PrintWriter(new File( // "/home/develop/eclipse_file/ACMproject/src/out")); PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out)); // Scanner in=new Scanner(new BufferedInputStream(System.in)); long a, b, c, l; a = in.nextLong(); b = in.nextLong(); c = in.nextLong(); l = in.nextLong(); long ans = (l + 1) * (l + 2) * (l + 3) / 6; for (int la = 0; la <= l; la++) { long up = Math.min(l - la, a - c - b + la); if (up >= 0) ans -= (up + 1) * (up + 2) / 2; } for (int lb = 0; lb <= l; lb++) { long up = Math.min(l - lb, b - c - a + lb); if (up >= 0) ans -= (up + 1) * (up + 2) / 2; } for (int lc = 0; lc <= l; lc++) { long up = Math.min(l - lc, c - b - a + lc); if (up >= 0) ans -= (up + 1) * (up + 2) / 2; } out.println(ans); in.close(); out.close(); } }
32 Gym 100342H(思维题
题目:给出点数n和边数m,要求构造出一个图,使得dijkstra算法中每个点的最短路变化次数之和最大。
思路:队友的构造感觉是非常优美啊。。。。首先,我们不妨设最后的距离是按照点标号升序的,那么dijkstra的改变次数有一个显然的上界,那就是前面的每个点都在该点扩展一次。现在我们要达到这个上界。那么我们先从1开始,考虑到顺序关系,可以向点i连长度为i-1的边,此时所有各点的距离正好是升序,这样下次要扩展的节点就是2,而扩展该节点要将其他后面的所有点的距离都减小,那么我们把2向所有后面的点连比当前距离恰小1的边(注意2的距离是0,而依次方法构造的所有点到1的最短路都是0,但是都要分i次减小)那么实际上我们从i到j的连的边长就是i-j-1(所有点最短路都是0)。