ZOJ Problem Set - 3172 Extend 7-day Vacation
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3204
题意:就是找两个点,使得路径上的点最多。
分析:刚看不知道从何下手,再仔细看看note就好了,note前半部分就是说图是连通的,因为说点是直接或则间接相连的,后半部分说明图没有环,因为说如果出去之后要回原来的点,必须经过一条至少两次。有这个的话就可以知道给的图是颗树,然后求树上两点之间的点数最多,即距离最大。那么可以用树形DP,假设当前的点为k,它的子树最大的深度为i和j,那么就可以知道当前的路径为i和j之间的和再加1(k这个点),然后更新MAX,然后把k和最大的子树路径递归上去,用来更新k的父节点就可以了,然后最后要判断MAX是否大于7,最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map> #include <stack> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=1234; vector<int> V[maxn]; int n,m; void init() { for(int i=1; i<=n; i++)V[i].clear(); int u,v; for(int i=1; i<n; i++) { scanf("%d%d",&u,&v); u++; v++; V[u].push_back(v); V[v].push_back(u); } } bool cmp(int a,int b) { return a>b; } int MAX; int DFS(int now,int fa) { vector<int> need; need.push_back(0); need.push_back(0); for(int i=0; i<V[now].size(); i++) { int u=V[now][i]; if(u==fa)continue; int p=DFS(u,now); need.push_back(p); } sort(need.begin(),need.end(),cmp); MAX=max(MAX,need[0]+need[1]+1); return need[0]+1; } void play() { MAX=0; DFS(1,-1); if(MAX<7)printf("Impossible\n"); else printf("%d\n",MAX); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 3156 Taxi
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3127
题意:给你n个人n<=100,m辆车,n<=m<=100,然后给你n+m个坐标代表人和车的位置,最后再给你个速度v,求出最少的时间,使得每个人都有一辆车。(其中每个人都是同步进行的)
分析:这种题的话,就是相当于把n个人匹配到m辆车的n辆中,可以用二分图匹配算法,匈牙利的话是O(VE)那怎么求这个最少时间呢,其实可以二分求,然后每次二分的时间t,我们可以用O(nm)的时间,求的用t的时间,n个人可以和m辆车的哪几辆匹配,然后建图,跑一边匈牙利,如果可以最大匹配的话,那么这个时间是ok的,那么我们把上限变为t-0.001,因为要保留两位小数,反之把下限变为t+0.001,继续二分,最后输出答案即可。复杂的为logT*(n*m+VE),还是ok的,因为n和m比较小,最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map> #include <stack> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123; struct he { double x,y; } Sky[2][maxn]; int n,m; double ai[maxn][maxn]; double l,r; double calT(int i,int j,double v) { double dis=sqrt((Sky[0][i].x-Sky[1][j].x)*(Sky[0][i].x-Sky[1][j].x)+(Sky[0][i].y-Sky[1][j].y)*(Sky[0][i].y-Sky[1][j].y)); return dis/v; } void init() { for(int i=1; i<=n; i++)scanf("%lf%lf",&Sky[0][i].x,&Sky[0][i].y); for(int i=1; i<=m; i++)scanf("%lf%lf",&Sky[1][i].x,&Sky[1][i].y); double v; scanf("%lf",&v); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { ai[i][j]=calT(i,j,v); } } r=2000000.0/v; } bool bi[maxn][maxn]; int Link[maxn]; bool vis[maxn]; bool DFS(int now) { for(int i=1; i<=m; i++) { if(bi[now][i]&&!vis[i]) { vis[i]=1; if(Link[i]==0||DFS(Link[i])) { Link[i]=now; return true; } } } return false; } void play() { double MIN=r; l=0; while(l<=r) { double mid=(l+r)/2; memset(bi,0,sizeof(bi)); for(int i=1; i<=n; i++) { for(int j=1; j<=m; j++) { if(ai[i][j]<=mid)bi[i][j]=1; } } memset(Link,0,sizeof(Link)); bool flag=1; for(int i=1; i<=n; i++) { memset(vis,0,sizeof(vis)); if(DFS(i))continue; flag=0; break; } if(flag) { MIN=min(MIN,mid); r=mid-0.001; } else { l=mid+0.001; } } printf("%.2f\n",MIN); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 3180 Number Game
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3221
题意:就是给你6个正整数,前面三个是结果的数字,后面三个是起始的数字,我们可以从起始的数据选择一个数,然后删除它,用另外两个的和减去以代替,然后一直反复,看能否到达结果的三个数字。
分析:这种题一看,如果从初始状态推结果状态是很复杂的,因为每一次都有3个数字可以选择,所以复杂度肯定太高了,所以考虑用结果的三个数字推初始数据,因为假设结果数字为a1 a2 a2且a1<a2<a3,如果a1+a2-1=a3,说明a3是推过来的,所以可以把a3改回原来的数字,假设为a'3,那么a'3怎么算呢,因为我们知道现在a2最大,那么可以以为a1+a'3-1=a2,所以a'3=a2-a1+1,所以把a3换了,然后一直重复看看,能不能推到起始的三个数字。这里如果我们有a1+a2-1=a3的话,那么我们只要a1和a2能找到2个数字一样就可以了。因为a3可以变为任意数,然后的话如果a1+a2-1!=a3,那么特殊处理一下就行了,记得循环终止,如果a1+a2-1恒等于a3的时候,那么就可以终止了,因为死循环了,最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <map> #include <stack> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; int ai[4],bi[4],T; void init() { for(int i=1; i<=3; i++)scanf("%d",&ai[i]); for(int i=1; i<=3; i++)scanf("%d",&bi[i]); } bool vis[4]; void play() { while(1) { sort(ai+1,ai+4); sort(bi+1,bi+4); if(ai[1]+ai[2]-1!=ai[3]) { if(ai[1]==bi[1]&&ai[2]==bi[2]&&ai[3]==bi[3]) { printf("Yes\n"); return; } else { printf("No\n"); return; } } int sum=0; vis[1]=vis[2]=vis[3]=0; for(int i=1; i<=2; i++) { for(int j=1; j<=3; j++) { if(ai[i]==bi[j]&&!vis[j]) { vis[j]=1; sum++; } } } if(sum>=2) { printf("Yes\n"); return; } ai[3]=ai[2]+1-ai[1]; if(ai[1]==1&&ai[2]==ai[3])break; } printf("No\n"); } int main() { scanf("%d",&T); while(T--) { init(); play(); } //cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 3348 Schedule
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3819
题意:就是有个人叫DD,他参见乒乓球比赛,然后要拿冠军,(就是赢得场数最多的人),给你m个初始的结果和下面还要比的场次,问DD能否拿冠军,其中只能有一个冠军(即不能有多个最大值)
分析:一看这道题,就知道贪心取DD能赢的最大值,即加入DD已经赢了p场,然后他下面还比了q场,贪心的话,他最多能赢p+q场,所以其余的人最多只能赢p+q-1场,因为不能有一样的最大值。先判断当前每个人已经赢了的场数,假设p+q=a1,别人当前赢得场数为a2。a3...an,如果a2到an其中有大于等于a1那么直接就是no了,因为不管怎么比,都不可能比a1小了如果都小于a1的话,就必须找个方法判断到底行不行?从前面可以知道第i个人最多只能赢a1-ai-1场,这样才小于a1,设为b2.b2..b3,然后假设第i个人和其他人除了DD一共比了k场,那么他最多只能赢bi场,因为限制。然后就是想什么方法判断每个人都满足。这里就用到了最大流算法。假设有个源点s,假如每对人i和j比了kij场,那么就有从s流kij的流量到i和j,我们可以定义为点vij,然后vij可以流向vi和vj,因为这个没有限制,但是可以定义为kij,因为最大了.最后我们可以定义个汇点,然后从vi流bi个流量到t,因为最大只能是bi,最后跑一边最大流,看它能不能是一个可行流,即除了和DD比的场数之外,所有的场数是否可以满足要求。最后啪啪啪就可以AC了。
/* Author:Chieh Because Of Coding */ /* O(E^2 V) E//边数 V//节点 //每次按最短的s-t路径进行增广 */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=5*12; map<string,int> M; struct he { int to,val,id; }; vector<he> V[3000]; int n,m; char ch1[12],ch2[12],ch3[12]; int win[maxn]; int next[maxn][maxn]; int Link[maxn][maxn]; void init() { for(int i=1; i<3000; i++) { V[i].clear(); } M.clear(); ch1[1]='D'; ch1[2]='D'; ch1[3]='\0'; M[ch1+1]=1; memset(win,0,sizeof(win)); memset(next,0,sizeof(next)); int st=1; for(int i=1; i<=m; i++) { scanf("%s%s%s",ch1+1,ch2+1,ch3+1); if(M[ch1+1]==0) { M[ch1+1]=++st; } if(M[ch2+1]==0) { M[ch2+1]=++st; } int t1=M[ch1+1]; int t2=M[ch2+1]; if(ch3[1]=='w') { win[t1]++; } else { win[t2]++; } } scanf("%d",&m); for(int i=1; i<=m; i++) { scanf("%s%s",ch1+1,ch2+1); if(M[ch1+1]==0) { M[ch1+1]=++st; } if(M[ch2+1]==0) { M[ch2+1]=++st; } int t1=M[ch1+1]; int t2=M[ch2+1]; if(t1>t2)swap(t1,t2); next[t1][t2]++; } for(int i=2; i<=n; i++) { win[1]+=next[1][i]; } } int fa[3000]; int idx[3000]; bool vis[3000]; bool BFS(int s,int t) { memset(vis,0,sizeof(vis)); queue<int> Q; Q.push(s); fa[s]=s; vis[s]=1; while(!Q.empty()) { int now=Q.front(); Q.pop(); if(now==t)return 1; for(int i=0; i<V[now].size(); i++) { int u=V[now][i].to; int w=V[now][i].val; if(w>0&&!vis[u]) { vis[u]=1; fa[u]=now; idx[u]=i; Q.push(u); } } } return 0; } void play() { for(int i=2; i<=n; i++) { if(win[i]>=win[1]) { printf("No\n"); return; } }//判断是否没开始就不能满足了 int st=1; for(int i=2; i<=n; i++) { for(int j=i+1; j<=n; j++) { Link[i][j]=++st; } } for(int i=2; i<=n; i++) { Link[i][i]=++st; } st++; LL com=0; for(int i=2; i<=n; i++) { for(int j=i+1; j<=n; j++) { if(next[i][j]!=0) { int val=next[i][j]; int v=Link[i][j]; com+=val; V[1].push_back((he) { v,val,V[v].size() }); V[v].push_back((he) { 1,0,V[1].size()-1 }); int u1=Link[i][i]; int u2=Link[j][j]; V[v].push_back((he) { u1,val,V[u1].size() }); V[u1].push_back((he) { v,0,V[v].size()-1 }); V[v].push_back((he) { u2,val,V[u2].size() }); V[u2].push_back((he) { v,0,V[v].size()-1 }); } } } for(int i=2; i<=n; i++) { int id=Link[i][i]; int val=win[1]-win[i]-1; V[id].push_back((he) { st,val,V[st].size() }); V[st].push_back((he) { id,0,V[id].size()-1 }); } LL MAX=0; while(BFS(1,st)) { int MIN=2*INF; int now=st; while(fa[now]!=now) { int f=fa[now]; int id=idx[now]; MIN=min(MIN,V[f][id].val); now=f; } MAX+=MIN; now=st; while(fa[now]!=now) { int f=fa[now]; int id=idx[now]; int idd=V[f][id].id; V[f][id].val-=MIN; V[now][idd].val+=MIN; now=f; } } if(MAX==com)printf("Yes\n"); else printf("No\n"); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 2892 Wavelet Compression
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1891
题意:给你一个最终的数组,求初始数组,初始数组转化为最终数组的过程如下。假设有a1,a2...a8
第一次:a1+a2,a3+a4,a5+a6,a7+a8,a1-a2,a3-a4,a5-a6,a7-a8这八个数,
第二次:a1+a2+a3+a4,a5+a6+a7+a8,a1+a2-a3-a4,a5+a6-a7-a8,a1-a2,a3-a4,a5-a6,a7-a8,
第三次:a1+a2+a3+a4+a5+a6+a7+a8,a1+a2+a3+a4-a5-a6-a7-a7,a1+a2-a3-a4,a5+a6-a7,a8,a1-a2,a3-a4,a5-a6,a7,a8;
知道最后一项的第一项是从a1加到a8的时候停止,然后给你最终数列,即第三次的结构,求a1~a8,
分析:先从简单的开始,就拿例子直接来,可以发现我们加入可以把第三项推到第二项,然后把第二项推到第一项,最后把第一项还原成最初项,答案就出来了。怎么把第三项还原成第二项?可以发现第三项前面两项相加除2就是第二项的第一个,相减除2就是第二项的第二个,然后其余的不变。然后第二项怎么还原成第一项,发现第二项的第一个加第三个除以2是第一项的第一个,相减除以2是第一项的第二个,那么规律就有了。假设第三项为第一层,我们只把i从1枚举到层数,然后ai+ai+层数/2就是下面的相对应的项,ai-ai+层数/2也是相对应的项,怎么知道相对应的项,,我是来个下标计算的,因为每次都是增加两个,且是递增的,所以只要把下标进行改变就可以知道对应的是下一层哪个项了,其中我还有个辅助数组,因为如果ai在计算时就改变肯定会影响最终 结果计算完成后,层数就要乘以2,因为是2倍改变,然后就可以啪啪啪,AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <stack> #include <map> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=300; int n; int ai[maxn]; int bi[maxn]; void init() { for(int i=1; i<=n; i++)scanf("%d",&ai[i]); } int ci[]= {1,2,4,8,16,32,64,128,256}; void play() { if(n==1) { printf("%d\n",ai[1]); return; } int e=0; for(int i=0; i<=8; i++) { if(ci[i]<<1==n) { e=i; break; } } for(int i=0; i<=e; i++) { int st=1; for(int j=1; j<=n; j++)bi[j]=ai[j]; for(int j=1; j<=ci[i]; j++) { int p=(ai[j]+ai[j+ci[i]])>>1; int q=(ai[j]-ai[j+ci[i]])>>1; bi[st]=p; bi[st+1]=q; st=st+2; } for(int j=1; j<=n; j++)ai[j]=bi[j]; } bool first=1; for(int i=1; i<=n; i++) { if(!first)printf(" "); first=0; printf("%d",ai[i]); } printf("\n"); } int main() { while(scanf("%d",&n)!=EOF) { if(n==0)break; init(); play(); } // cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 2912 Average distance
飞机票:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1911
题意:就是给你一颗树包含n个点(n<=10000)和边权值,求任意两点之间的平均距离,即任意两点距离和除以有多少个点对。
分析:容易计算点对数就是n*(n-1)/2,如何求总距离和,可以考虑两个点,假设为u,v,且有条边(u,v)权值为x,这样我们就可以知道,x肯定要用u子节点的个数*v子节点的个数(其中子节点代表u下面所有的节点个数),包含各自本身,因为u子节点到v子节点必定要经过(u,v)这条边,这样的话我们就可以递归处理了,假设当前节点为u,它的子节点数加它本身有p个,则它父亲节点v那一块就有n-p个。然后总距离就加上p*(n-p)*x了。最后啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=12345; int T; vector<int> V[maxn]; vector<int> G[maxn]; int n; void init() { int u, v,w; scanf("%d",&n); for(int i=1; i<=n; i++) { V[i].clear(); G[i].clear(); } for(int i=1; i<n; i++) { scanf("%d%d%d",&u,&v,&w); u++; v++; V[u].push_back(v); V[v].push_back(u); G[u].push_back(w); G[v].push_back(w); } } double ans; int DFS(int now,int fa,double val) { int chi=1; for(int i=0; i<V[now].size(); i++) { int u=V[now][i]; int w=G[now][i]; if(u==fa)continue; chi+=DFS(u,now,w); } int oth=n-chi; ans=ans+(val*oth*chi); return chi; } void play() { ans=0; DFS(1,-1,0); double fm=(n*(n-1)/2); printf("%.10f\n",ans/fm); } int main() { scanf("%d",&T); while(T--) { init(); play(); } //cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 3468 Dice War
飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4254
分析:其实这个题难懂的是题意啊~~~后来百度下骰子游戏菜知道这个题的意思。。先来看下骰子游戏的规则
规则略微有点复杂,刚开始可能不太明白,我详细解释下:
你的终极目标是占领所有的地块,你的颜色是紫色。
占领地块的方法就是用你的地块攻击相邻的地块,胜负由这两个地块上的骰子掷出点数决定。
1)如果你赢了,那么原来地块上的骰子变成一个,新占领地块上的骰子数 = 原来地块骰子数 - 1 ;
2)如果平了或者输了,那么你的地块上的骰子也会变成一个。
只要你的地块上的骰子数大于一个,就可以攻击别人。
在攻击结束时,点击 "End Turn" 可以补充兵力,补充的总数是你此时连在一起的地块的总数,分配则是随机的。每个地块上最多有 8 颗骰子。
提示策略:刚开始的时候尽量把自己的地都连到一起,中期的时候怎样布阵对于进攻和防守都很重要,后期基本都是八颗八颗的硬碰硬了 :P
看了应该就秒懂了吧。反正题目就是说给你攻击者的骰子数和防御者的骰子数。然后求攻击者赢得概率(就是说攻击者的点数严格大于防御者的点数)。。。这里要注意:如果攻击者的骰子数是1,那么就不能攻击,意思就是说赢得概率是0。。。然后你就可以根据概率来搞了。。。对于数学概率的渣渣,,,只有这么一个想法就是说把攻击者的点数枚举。。。怎么枚举法??
已知n,那么攻击者的点数就是在n~6*n之间。。。然后枚举每个点数所占的概率。。。就是 k的可能方法有几种/6^n就是k的概率、、然后你要赢:那么就是m~6*m之间小于k的概率 那么就是(m~k-1)之间所有可能的方法/6^m的概率、、、然后从将所有的相加就是答案。。。这里计数方法,我先是DFS。。。其实也不慢,但是也不快。。。后来想到了DP。。。就是按骰子数来DP,然后当前骰子有6中可能1~6,然后看前面的是否出现过k,出现过,那么当前(k+(1~6))就要累加了。。。这样复杂度就直接变为(6*n)^2
/* Author:Chieh Grow up happy */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <vector> #include <stack> #include <map> #define LL long long using namespace std; int ai[2][9][49]; int bi[2]; int p; void play() { if(bi[0]<=1) { printf("0\n"); return; } for(int z=0; z<=1; z++) { int e=6*bi[z]; memset(ai[z][0],0,sizeof(ai[z][0])); ai[z][0][0]=1; for(int j=1; j<=bi[z]; j++) { memset(ai[z][j],0,sizeof(ai[z][j])); for(int i=1; i<=6; i++) { for(int k=0; k<=e; k++) { if(ai[z][j-1][k]>0) { ai[z][j][k+i]+=ai[z][j-1][k]; } } } } } int fm=pow(6.0,bi[0]); int fm1=pow(6.0,bi[1]); for(int i=1; i<=6*max(bi[0],bi[1]); i++) { ai[1][bi[1]][i]=ai[1][bi[1]][i]+ai[1][bi[1]][i-1]; } double over=0; for(int i=1; i<=6*bi[0]; i++) { double pop=((double)(ai[0][bi[0]][i]))/fm; double pop1=((double)(ai[1][bi[1]][i-1]))/fm1; over=over+(pop*pop1); } printf("%.8lf\n",over); } int main() { while(scanf("%d%d",&bi[0],&bi[1])!=EOF) { play(); } return 0; }
ZOJ Problem Set - 1990 Subway Tree Systems
飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=990
由于昨天有事。。。所以没怎么细想。。。然后一直以为只要深度相同。。。然后子节点个数相同就可以了。。。然后就一直Wa。。。后来自己把自己的想法推翻了。。。直接看下图:
然后这里的话。。。如果按我起先的想法肯定是wa的因为1层都有2个子节点。二层有3个节点和2个节点。三层有2,0,0,3,0个节点
但是这两棵树明显不同、、debug爽啊。。。接着我的想法就是。。。那把每个节点的深度算出来和他的子树节点的深度都算出来。。。然后排序比较。。。这个想法明显是正确。。但是速度不快啊~~~囧。然后百度了一下。。。有一种方法是跟括号匹配差不多。。。就是说经过父节点(,回到了父节点就),然后括号里面的可以交换位置。。。交换出最小字典序。。。然后比较是否相同,还有一种方法:好像说是只要把深度算出来然后计算子树节点数排序比较就可以了。。。但是为什么呢。。感觉也不怎么对。。。画个图。。。其实这个想法是错误的
这个的序列是0010101001011100101011和0010101010101100010111。。。然而这个是diff。但是前面的跑出来的是same。。所以错误的。。。估计测试数据没有这个数据。我还是不传输错误的代码了。。。就用那个速度慢的吧
/* Author:Chieh Grow up happy */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <vector> #include <stack> #define LL long long using namespace std; const int maxn=3456; char ch[2][maxn]; struct he { int dep; vector<int> V; } JLJ[2][1567]; void init() { scanf("%s%s",ch[0]+1,ch[1]+1); } int idx,st, p; int len; void DFS(int now,int dep) { JLJ[p][now].dep=dep; while(idx<=len&&ch[p][idx]=='0') { idx++; st++; int q=st; DFS(q,dep+1); for(int j=0; j<JLJ[p][q].V.size(); j++) { JLJ[p][now].V.push_back(JLJ[p][q].V[j]+1); } } JLJ[p][now].V.push_back(0); sort(JLJ[p][now].V.begin(),JLJ[p][now].V.end()); idx++; return; } bool cmp(he a,he b) { if(a.dep!=b.dep)return a.dep<b.dep; if(a.V.size()!=b.V.size())return a.V.size()<b.V.size(); for(int i=0; i<a.V.size(); i++) { if(a.V[i]>b.V[i]) { return 1; } else if(a.V[i]<b.V[i]) { return 0; } } return 0; } void play() { if(strlen(ch[0]+1)!=strlen(ch[1]+1)) { printf("different\n"); return; } len=strlen(ch[0]+1); for(int i=0; i<=len/2; i++) { JLJ[0][i].dep=0; JLJ[0][i].V.clear(); JLJ[1][i].dep=0; JLJ[1][i].V.clear(); } idx=1; st=0; p=0; DFS(0,0); idx=1; st=0; p=1; DFS(0,0); sort(JLJ[0],JLJ[0]+st+1,cmp); sort(JLJ[1],JLJ[1]+st+1,cmp); //cout<<st<<endl; for(int i=0; i<=st; i++) { if(JLJ[0][i].dep!=JLJ[1][i].dep) { printf("different\n"); return; } if(JLJ[0][i].V.size()!=JLJ[1][i].V.size()) { printf("different\n"); return; } for(int j=0; j<JLJ[0][i].V.size(); j++) { if(JLJ[0][i].V[j]!=JLJ[1][i].V[j]) { printf("different\n"); return; } } } printf("same\n"); } int n; int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { init(); play(); } //cout << "Hello world!" << endl; return 0; }
ZOJ Problem Set - 3665 Yukari's Birthday
飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4888
分析:起先想要把所有可能求出来。。。然后二分答案。。。但是内存不够呀然后各种优化啊。。。还是不行啊。。。果然是彩笔~接着就想着暴力。。。暴力你妹啊!!!各种TLE啊~~~没办法了。。想着在线二分。。。但是二分k还是r呢。。。第一直觉是k。。然后就往k里面想了。。。发现k无法正常二分。。。必需要有什么东西可以限制k的大小就可以二分。。。想想r不是很小么40...那就用r来限制k二分。。。接着就是各种悲剧渣死了。。。各种Wa。。。后来看了别人的题解。。。发现我二分里面有些是多余的。。。果然是自己贱啊但是想法还是正确的。。最后改了几个地方就AC了
/* Author:Chieh Grow up happy */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <stack> #include <vector> #define LL long long using namespace std; LL n; LL cal(int e) { LL l=2,r=min(n,1000000LL); while(l<=r) { LL mid=(l+r)/2; LL over=1; LL tm=1; for(int i=1; i<=e; i++) { tm=tm*mid; over+=tm; if(over>n+1)break; } if(over==n+1||over==n)return mid; if(over>n+1)r=mid-1; else { l=mid+1; } } return 0; } void play() { LL a=1,b=n-1; for(int i=2; i<=39; i++) { LL t=cal(i); if(t==0)continue; if(t*i<a*b) { a=i; b=t; } else if(t*i==a*b) { if(a>i) { a=i; b=t; } } } printf("%lld %lld\n",a,b); } int main() { while(scanf("%lld",&n)!=EOF) { play(); } return 0; }
ZOJ Problem Set - 3422 Go Deeper
飞机票: http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4106
今天起来一道2-sat。。。巩固知识~~~
分析:题目的意思就是说求函数最大能递归到第几层。。。告诉你a,b,c数组。。。x数组未知。。。题目秒懂。。。那个程序就不解释了,ACMer肯定能够看得懂:).这种题该怎么搞??其实这里发现一个限制递归的地方就是
if dep < m and x[a[dep]] + x[b[dep]] != c[dep] then go(dep + 1, n, m)
那个<m就不用讲了。。如果>=m就溢出a,b,c数组了。。。那么其实就是后面的那个条件。。。我们二分0~(m-1)。假设当前的值是mid。。。那么就可以加边了。。。循环从0~mid。。。取出a[k]和b[k],c[k]的值(语文有限。。。望看懂~)那么如果c[k]的值是0.那么x[a[k]]+x[b[k]]的值至少有一个是1 如果是c[k]是1 那么两个值必须相同。。。如果是2 那么两个值肯定不同。。这里发现其实这个跟那个位运算2-sat题型差不多。。。然后把边添加了跑一下tarjan。。。如果不行。。说明递归层太深、、、r=mid-1.如果可以,那么l=mid+1...这样找到最大值即可~啪啪啪。。。TLE(vector忘记清0了。。。bug啊!!!)最后只要把深度+1就可以AC了
/* Author:Chieh Grow up happy */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <queue> #include <stack> #include <vector> #define LL long long using namespace std; const int maxn=456; const int Maxn=12345; struct he { int a,b,c; } JLJ[Maxn]; vector<int> V[maxn]; int n,m; void init() { scanf("%d%d",&n,&m); for(int i=0; i<m; i++) { scanf("%d%d%d",&JLJ[i].a,&JLJ[i].b,&JLJ[i].c); } } bool instack[maxn]; int dfn[maxn],low[maxn],sta[maxn],belong[maxn],indexx,scnt,cntnum; void tarjan(int u) { dfn[u]=low[u]=indexx++; instack[u]=1; sta[++scnt]=u; for(int i=0; i<V[u].size(); i++) { int v=V[u][i]; if(dfn[v]==-1) { tarjan(v); low[u]=min(low[u],low[v]); } else if(instack[v])low[u]=min(dfn[v],low[u]); } if(dfn[u]==low[u]) { for(int v=-1; v!=u; scnt--) { v=sta[scnt]; instack[v]=0; belong[v]=cntnum; } cntnum++; } } void play() { int l=0; int r=m-1; int over=0; while(l<=r) { int mid=(l+r)/2; for(int i=0;i<=2*n;i++)V[i].clear(); for(int i=0; i<=mid; i++) { int u=JLJ[i].a*2; int v=JLJ[i].b*2; int t=JLJ[i].c; if(t==2) { V[u].push_back(v^1); V[v].push_back(u^1); } else if(t==1) { V[u].push_back(v); V[v].push_back(u); V[u^1].push_back(v^1); V[v^1].push_back(u^1); } else { V[u^1].push_back(v); V[v^1].push_back(u); } } indexx=cntnum=0; scnt=-1; memset(dfn,-1,sizeof(dfn)); memset(instack,0,sizeof(instack)); for(int i=0; i<2*n; i++) { if(dfn[i]==-1)tarjan(i); } bool flag=1; for(int i=0; i<n; i++) { if(belong[i*2]==belong[i*2+1]) { flag=0; break; } } if(!flag) { r=mid-1; continue; } over=max(over,mid); l=mid+1; } printf("%d\n",over+1); } int T; int main() { scanf("%d",&T); while(T--) { init(); play(); } // cout << "Hello world!" << endl; return 0; }