HDU 5276 YJC tricks time
飞机票:http://acm.hdu.edu.cn/showproblem.php?pid=5276
题意:就是给你时钟的分针和时针的角度,且乘上12000,求现在的时间是几时几分几秒,且秒要是10的倍数。而且题目给的是劣角的角度,即不会大于12000*180,
分析:枚举时间的话肯定不会超时,怎么判断角度呢,我们可以先求出分针的角度,加入当前的时间是i:j:k,且k是10的倍数,那么分针的角度就是j*6+0.1*k,因为一秒分钟转0.1度,分针一分钟转6度,可以知道j*6+0.1*k是整数,假设是q。我们知道时针转的角度是i*30,因为小时转30度,且每分钟转1/360*30,所以总的转了i*30+30*q/360,因为有精度误差,因为30*q不一定能整除,但是题目给了乘上12000,所以可以直接提高倍数,就是q=12000*q,设时针角度为p,所以p=i*30*12000+30*q/360,因为q已经乘过了,所以不用乘了。这样就可以保证q和p都是整数,但是还有个问题就是劣角,所以一共有两个角。一个是abs(q-p),一个是360*12000-abs(q-p),因为扩大了12000,只要其中一个和输入的角度一样就是对的时间。中间不要用double,直接int,因为我double就wa,int就AC,然后啪啪啪就可以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 n; void init() { } void play() { for(int i=0; i<=11; i++) { for(int j=0; j<=59; j++) { for(int k=0; k<=59; k+=10) { int q=j*6; q+=0.1*k; q*=12000; int p=i*30*12000; p+=q*30/360; int s1=abs(q-p); int s2=360*12000-s1; if(s1==n||s2==n) { printf("%02d:%02d:%02d\n",i,j,k); } } } } } int main() { while(scanf("%d",&n)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R311 DIV2 D. Vitaly and Cycle
飞机票:http://codeforces.com/contest/557/problem/D
题意:给你n个点和m条边的无向图,可能不是连接在一起的,并且没有子环和平行边,求最小添加几条边可以使它包含奇数点的圈,并且求满足这样方案的方法有多少种,两种方案不同即边的设置不完全一样。
分析:起先可以很容易知道最少的边不会超过3,即0,1,2,3,为什么?加入所有的点都不相连,即m等于0,那么肯定对任意三点添加三条边就可以有奇数点的圈,总方案就是C(n,3)即n*(n-1)*(n-2)/3;然后就可以用染色的方法对0,1,2进行判断,具体怎么判断,假设当前点为i,且i没有访问过,那么对i进行DFS,可以设i点的颜色为0,与i相连的点颜色为1,并且对i相连的没有访问过的边继续按照这个染色方法染色,即当前点的颜色为0,则相连的边为1,反之同理。假设当前的节点为now,它的邻边v被访问过,如果它们的颜色一样的话,就说明包含奇数点的圈,为什么?因为点是根据0,1,0,1染色的,因为两个相邻的颜色一样,说明,就是1,0,1,0,....1,因为没有最后一个点,它就是偶数个,包含了,它就是奇数个,只要是这样,就说明不用添加边,则为0条边,一种方法。反之的话,继续搜索,并且记录,染色为1的点的个数,和染色为0的点的个数。当搜索完的时候,得到为1的点为p,为0的点的数量为q,我们要使它包含奇数个点,所以我们要选择两个点,且它们之间的数量为奇数,因为我们知道只要两个点的颜色一样,则它们之间肯定包含了奇数个点的圈,所以我们从p里选择两个,即C(p,2)=p*(p-)/2,同理从q中选两个C(q,2)=q*(q-)/2,加到总方法里去,对每一块都分别进行统计,当计算完的时候,当总数为0的时候,又因为m不等于0,所以最多是2个点相连,所以总数就是m*(n-2),即m里选择条边,n-2选择一个点,然后输出,这时候我们必须添加两条边,如果总数大于0,则最多只要添加一条边,输出答案就好,最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; 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<=m; i++) { scanf("%d%d",&u,&v); V[u].push_back(v); V[v].push_back(u); } } bool vis[maxn]; bool color[maxn]; LL cc[2]; bool flag; LL MIN; void DFS(int now) { vis[now]=1; cc[color[now]]++; for(int i=0; i<V[now].size(); i++) { int v=V[now][i]; if(!vis[v]) { color[v]=!color[now]; DFS(v); } else if(color[v]==color[now]) { flag=1; } } } void play() { if(m==0){ LL ans=1LL*n*(n-1)*(n-2)/6; printf("3 %I64d\n",ans); return; } memset(vis,0,sizeof(vis)); flag=0; LL ans=0; memset(color,0,sizeof(color)); for(int i=1; i<=n; i++) { if(vis[i])continue; cc[0]=cc[1]=0; DFS(i); if(flag){ printf("0 1\n"); return; } ans+=cc[0]*(cc[0]-1)/2; ans+=cc[1]*(cc[1]-1)/2; } if(ans==0){ printf("2 %I64d\n",1LL*m*(n-2)); return; } printf("1 %I64d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R311 DIV2 C. Arthur and Table
飞机票:http://codeforces.com/contest/557/problem/C
题意:给你个桌子,有n个桌脚(n<=1e5),每个桌脚有个高度为ai,拆掉每个桌脚需要力量bi,现在问你拆除桌脚使桌子稳定,且用力最小,桌子稳定的条件是最大的那个脚的总数必须大于当前脚数量的一半,假设有当前有5个桌脚,要使稳定,需要3个最大的脚,假设4个,则要3个最大脚,只有1个他就是稳定的。
分析:我们可以枚举脚的长度,怎么枚举??按脚长度排个序,然后假设长度为k的脚的数量从l到r,即有r-l+1个为k的数量。然后我们可以来个辅助数组ci,就是大于k脚长度需要花费的总力气,这个力气可以从后往前推,因为我们是从小到大对脚长度进行排序,那么初始必须花费的力气是c[r+1],然后我们有r-l+1的数量,设为sum,则我们可以保留sum-1(因为大于一半)个脚,因为前面有l-1个脚,我们可以拆除l-1-(sum-1)个脚。设为des,如果des<=0,则不用拆了,因为我们已经足够,如果大于0,则要从前面l-1选择des个脚拆除,我们贪心的话,肯定会拆需要力量最小的脚,因为力量<=200,所以我们可以用个数组need[200]表示前面各个力量有多少个然后从小到达选择des个,设为力量需要为p,这总共需要力量c[r+1]+p,然后去最小值因为我们要处理need[200],所以我们要一个辅助数组记录l到r需要的各个力量,然后处理完l~r则把辅助数组的数据加到need上去,让下一次可以继续迭代,复杂度O(n*200),最后啪啪啪就可以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=123456; int n; struct he { int len,val; } Sky[maxn]; bool cmp(he a, he b) { return a.len<b.len; } int bi[maxn]; int ai[234]; int ci[234]; void init() { for(int i=1; i<=n; i++) { scanf("%d",&Sky[i].len); } for(int i=1; i<=n; i++) { scanf("%d",&Sky[i].val); } sort(Sky+1,Sky+1+n,cmp); bi[n+1]=0; for(int i=n; i>=1; i--) { bi[i]=Sky[i].val; bi[i]+=bi[i+1]; } Sky[n+1].len=INF; Sky[0].len=0; } void play() { memset(ai,0,sizeof(ai)); int l=-1; int MIN=INF; memset(ci,0,sizeof(ci)); for(int i=1; i<=n+1; i++) { if(Sky[i].len!=Sky[i-1].len) { if(l!=-1) { int ok=(i-1-l)+1; int xy=ok-1; xy=l-1-xy; int sum=bi[i]; if(xy>0) { for(int j=1; j<=200; j++) { if(xy<=ai[j]) { sum+=xy*j; break; } else { xy-=ai[j]; sum+=ai[j]*j; } } } if(sum<MIN)MIN=sum; } for(int j=1; j<=200; j++) { ai[j]+=ci[j]; } l=i; memset(ci,0,sizeof(ci)); int v=Sky[i].val; ci[v]++; } else { int v=Sky[i].val; ci[v]++; } } printf("%d\n",MIN); } int main() { while(scanf("%d",&n)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R310 DIV2 D. Case of Fugitive
飞机票:http://codeforces.com/contest/556/problem/D
题意:给你n个区间li,ri,且ri<li+1,(1<=i<n),li<=ri,然后给你m座桥,每个有个长度ai,然后要求把桥搭在区间上,且满足桥的长度
大于等于li+1-ri,且小于等于ri+1-li,求能否用桥把所有的相邻区间连接起来,并且输出每个相邻区间对应的桥下标。
分析:起先一看以为是二分图,但是看边太多直接放弃。这里我们 可以知道一个东西。就是相邻区间需要的桥的最大值和最小值,假设两个相邻区间li,ri li+1,ri+1,我们定义他们的坐标为i,则可知需要的最小长度为li+1-ri,最大长度为ri+1-li,假设为最小为le最大为ri,接着我们再把m条桥排序,从小到大。接着运用贪心看看怎么贪?假设当前要判断桥i属于哪个区间le ri,我们必须把所有可以用桥i的区间找出来,然后找右端点最小的,为什么?因为大伙都可以用它,当然我们要使用剩余使用量最小的来接受它,不然给别人,它都亏。搞到这里就知道怎么贪了吧。就是把le ri按左区间排序,如果一样不用管。然后来个优先队列,假如当前的le<=桥i的长度,就把它加到优先队列,为什么??因为我们不知道它满不满足,但是只要它的右端点最小,然后比桥i的长度还短,那么直接就No了,因为别人都比它好。优先队列干嘛用的?就是取右端点最小的,因为我们前面判断过的比较小的le可能要在这个时候用到。总体复杂的就是O(n*logn)了。然后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=2*123456; LL l[maxn],r[maxn]; struct he { int id; LL l,r; bool operator<(const he& a)const { return r>a.r; } } Sky[maxn]; LL ai[maxn]; struct he1 { LL val; int id; } Sky1[maxn]; int can[maxn]; int n,m; bool cmp(he1 a,he1 b) { return a.val<b.val; } void init() { for(int i=1; i<=n; i++) { scanf("%I64d%I64d",&l[i],&r[i]); } for(int i=1; i<=m; i++) { scanf("%I64d",&Sky1[i].val); Sky1[i].id=i; } sort(Sky1+1,Sky1+1+m,cmp); } bool cmp2(he a,he b) { if(a.l!=b.l)return a.l<b.l; return a.r<b.r; } priority_queue<he> pq; void play() { int st=0; for(int i=2; i<=n; i++) { LL a1=l[i]-r[i-1]; LL a2=r[i]-l[i-1]; st++; Sky[st].l=a1; Sky[st].r=a2; Sky[st].id=st; } sort(Sky+1,Sky+1+st,cmp2); while(!pq.empty())pq.pop(); int now=1; for(int i=1; i<=m; i++) { int id=Sky1[i].id; LL val=Sky1[i].val; while(now<=st) { if(Sky[now].l<=val) { pq.push(Sky[now]); now++; } else { break; } } if(pq.empty())continue; he aa=pq.top(); pq.pop(); if(aa.r<Sky1[i].val) { printf("No\n"); return; } int idx=aa.id; can[idx]=id; } if(!pq.empty()||now!=st+1) { printf("No\n"); return ; } printf("Yes\n"); for(int i=1; i<=st; i++) { printf("%d ",can[i]); } printf("\n"); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R310 DIV2 C. Case of Matryoshkas
飞机票:http://codeforces.com/contest/556/problem/C
题意:就是给你k个已经按大小递增的序列,每个有mi个1<=mi<=n,m1+m2+,,mk=n,其中1到n只出现一次。求组合成从1到n的序列最小的时间值,具体的每秒能做的事是,把序列拆掉或把序列合并,拆掉的话必须有一个是独立的不能两个子序列一起拆开,比如序列1-2-3-4,可以把1或4拆下来,但是不能拆成1-2 3-4这样是非法的。合并的话也是有规律的,就是以1为首的字串不能何在一个序列长度大于1的序列里面,这样也是非法的,比如序列1-2-3 和序列5-6合并成1到6要3秒,因为5-6包含2长度,所以要拆开,成为5和6,然后5合并,6合并。题意比较坑爹啊~
分析:知道题意后就可以瞎搞了,很简单,就是把包含1的序列后面能递增到几个拿出来先,如果后面没了,初始时间就是0,如果后面还有就是1,因为要拆开,然后后面的序列都要拆,因为要把包含1的合并进去,所以假设序列长度为len,则拆要len-1,合并要len,所以共要len*2-1,所有加起来就是答案,然后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; int idx[maxn]; vector<int> V[maxn]; int first[maxn]; int n,k; LL MIN; void init() { int m; MIN=0; for(int i=1; i<=k; i++) { V[i].clear(); first[i]=0; scanf("%d",&m); bool flag=1; for(int j=1; j<=m; j++) { int u; scanf("%d",&u); if(u==1) { flag=0; int p=1; for(int z=j+1; z<=m; z++) { j=z; scanf("%d",&u); if(u==++p)continue; MIN+=(m-z+1)*2; break; } } } if(flag){ MIN+=(m*2)-1; } } } void play() { printf("%I64d\n",MIN); } int main() { while(scanf("%d%d",&n,&k)!=EOF) { 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; }
HDU 5273 Dylans loves sequence
飞机票:http://acm.hdu.edu.cn/showproblem.php?pid=5273
题意:给你N个数ai 1<=i<=n,N<=1000,以及Q个查询,Q<=100000,每个查询有两个整数L,R 求[L,R]逆序对的数量(逆序对就是当i<j 时,ai>aj)
分析:要求[L,R]之间的逆序对数量,且Q<=100000,我们可以O(n^2)的时间求出[i,j]之间的逆序对的数量,i<=j,然后用O(1)的时间查询答案,具体怎么算,我们先来个辅助数组fi[maxn][maxn],其中maxn是题目给的最大N,因为N最大是1000,所以数组大小为1000000,不会爆空间,然后我们令fi[i][j],表示以i为逆序对的端点,然后计算j从i~1,以j为右端点的总数,用递推就可以在O(n^2)算出来,接着来个sum[maxn][maxn]数组,这个数组就是答案,其中sum[i][j]表示从i到j一共用多少组逆序对,因为我们有fi数组,所以只要累加fi数组就可以了,j从i到n,然后sum[i][j]=sum[i][j-1]+fi[j][i];这个怎么解释,好好想想就知道了,假设我们要知道从i到j的sum,因为我们已经知道sum[i][j-1]的sum了,而以j结尾,且已i开始的之间的总数就是fi[j][i],所以加上就是答案,复杂度O(n*n+Q),然后啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=1234; LL ai[maxn]; LL fi[maxn][maxn]; LL sum[maxn][maxn]; int n,Q; void init() { for(int i=1; i<=n; i++)scanf("%I64d",&ai[i]); } void play() { memset(fi,0,sizeof(fi)); memset(sum,0,sizeof(sum)); for(int i=1; i<=n; i++) { fi[i][i]=0; for(int j=i-1; j>=1; j--) { fi[i][j]=fi[i][j+1]; if(ai[i]<ai[j]) { fi[i][j]++; } } } for(int i=1;i<=n;i++){ sum[i][i]=0; for(int j=i+1;j<=n;j++){ sum[i][j]=sum[i][j-1]+fi[j][i]; } } int L,R; while(Q--) { scanf("%d%d",&L,&R); printf("%I64d\n",sum[L][R]); } } int main() { while(scanf("%d%d",&n,&Q)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R308 DIV2 E. Vanya and Brackets
飞机票:http://codeforces.com/contest/552/problem/E
题意:给你个没有括号的正确表达式,其中只包含*和+,和1~9的数字,添加一个括号,使得最后的数字最大。
分析:假设一共有n个数字,其实就是在选择两个标i和j(i<=j)使得1~i-1和i~j和j+1~n的计算最大。具体可以维护前缀和后缀,具体需要维护哪些值,前缀的话,假设从1~i,我们维护这个值的大小,但是还不够,因为我们不知道紧接的是*还是+,如何是+的话还是很好处理的,只要把两个相加就好了,如果是乘,我们还得维护1~i最后一个+后面的数字的大小,因为我们要把它与后面的数字相乘然后后缀也是一样的,维护相同的值,不一样的是方向相反,因为我们如果从前面开始,后缀就没办法维护了。具体求数组的方法是使用栈来求(这里就不细讲,是每个人应该会的)假设我们已经求的前缀的和li和前缀要维护最右边的数字lvali 和后缀的和ri和维护最左边的值rvali,接下来就是枚举需要加括号的地方。假设为在i前面加和在j后面加。则有
求的前缀到i-1的值为 ls,后缀到j+1的值为rs,且lval为v1 ,rval为v2,中间的i到j的结果为s。
(1)如果i前面的符号是*和j后面的符号是*,则大小sum就为ls+rs-v1-v2+v1*s*v2
因为v1和v2要和s乘为新的数,所以要减去。
(2)如果只有i前面的符号是*,则大小sum为ls-v1+v1*s+rs;
因为后面的话是直接加上去的。
(3)如果只有j+1后面的符号为*,则跟(2)差不多性质
(4)如果i从1开始,则只要考虑rs的情况
(5)如果j到了n,则只要考虑ls的情况
复杂度为O(n*n)然后啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=5678; char ch[maxn]; int ai[maxn]; int bi[maxn]; int len; LL lr[2][maxn]; LL limit[2][maxn]; int st; void get() { for(int i=0; i<2; i++) { int s=1; int c=1; int cz=-1; if(i==1) { s=st; c=-1; cz=0; } lr[i][s]=ai[s]; vector<LL> V; LL sum=ai[s]; V.push_back(ai[s]); limit[i][s]=ai[s]; for(int j=1; j<st; j++) { s=s+c; if(bi[s+cz]==1) { LL q=V[V.size()-1]; LL p=q*ai[s]; V[V.size()-1]=p; sum=sum-q+p; } else { V.push_back(ai[s]); sum=sum+ai[s]; } lr[i][s]=sum; limit[i][s]=V[V.size()-1]; } } } void init() { len=strlen(ch+1); st=0; for(int i=1; i<=len; i++) { if(ch[i]=='*') bi[st]=1; else if(ch[i]=='+') bi[st]=0; else { st++; ai[st]=ch[i]-'0'; } } get(); } void play() { LL MAX=0; for(int i=1; i<=st; i++) { vector<LL>V; LL sum=0; for(int j=i; j<=st; j++) { if(j==i) { V.push_back(ai[i]); sum=ai[i]; } else { if(bi[j-1]==1) { LL q=V[V.size()-1]; LL p=q*ai[j]; V[V.size()-1]=p; sum=sum-q+p; } else { V.push_back(ai[j]); sum=sum+ai[j]; } } int ln=i-1; int rn=j+1; LL ll=lr[0][ln]; LL rr=lr[1][rn]; if(ln>=1&&rn<=st) if(bi[ln]==1&&bi[j]==1) MAX=max(MAX,ll+rr-limit[0][ln]-limit[1][rn]+limit[0][ln]*limit[1][rn]*sum); else if(bi[ln]==1) MAX=max(MAX,ll+rr-limit[0][ln]+limit[0][ln]*sum); else if(bi[j]==1) MAX=max(MAX,ll+rr-limit[1][rn]+limit[1][rn]*sum); else MAX=max(MAX,ll+rr+sum); else if(ln>=1) if(bi[ln]==1) MAX=max(MAX,ll-limit[0][ln]+limit[0][ln]*sum); else MAX=max(MAX,ll+sum); else if(rn<=st) if(bi[j]==1) MAX=max(MAX,rr-limit[1][rn]+limit[1][rn]*sum); else MAX=max(MAX,rr+sum); else MAX=max(MAX,sum); } } printf("%I64d\n",MAX); } int main() { while(scanf("%s",ch+1)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }