A题:
题目链接:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1465
题目描述:
将含有n个整数的数列A中的所有整数重新从小到大排序以后得到的新的数列B, 满足,
对于任意正整数i, j(1 <= i, j <= n), 恒有Bj - Bi = j - i 。现在, 假定给你一
个整数列C, 你需要求出将其变为连续数列所需花费的最小代价和。(将整数x变为整数
y需要花费|x - y|的代价, 所谓代价和, 指的是改变所有数字所需花费的代价的总和)。
解题思路:
设新数列B的第一个数为x,那么对于A数列来说,x取值太大,花费的代价会变大,x取值太小
花费也会变大,所以对于:花费=f(x)这个函数来说,这是一个先下降后上升的凹曲线,对于
单调的曲线我们可以二分,对于凹凸曲线我们可以用三分,具体的做法是在一个(l,r)区间里取
两个点把区间分成三等分,再求这两个点的具体数值,把对我们不利的那个点到它最近的端点
的那段区间去掉,因为如果这个点对我们要求的值不是有利的,那么它到它最近的那个端点之间
的那一段都不会出现更有利的(因为这个曲线是凹凸的,大家画个图就什么都明白了,否则如果
出现更有利的,那么这个点就不是对我不利的了).
这题刚才在一个群里看到了另一种解法:把B数列的Bj-Bi=j-i变换成Bj-j=bi-i,然后就是一个求这个
数列的中位数问题了;
//共享部分 #include<math.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; long long A[110000]; //解法一:三分 long long F(long long x,long long n){ long long i,sum=0; for(i=1;i<=n;i++) sum+=abs(A[i]-(x+i-1)); return sum; } long long find(long long n) { long long mid1,mid2,l=-2000000000,r=2000000000,c=0,v; while(c<50){ c++; mid1=(l+r)/2; mid2=(l+mid1)/2; if(F(mid1,n)<F(mid2,n)) l=mid2; else r=mid1; } v=1;v<<=62; for(l=mid2-5;l<=mid1+5;l++) if(F(l,n)<v) v=F(l,n); return v; } int main() { int n,i; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) scanf("%lld",&A[i]); sort(A+1,A+n+1); printf("%lld\n",find(n)); } return 0; } //解法二:变换构造 int main() { int n,i; long long ans; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) scanf("%lld",&A[i]); sort(A+1,A+n+1);ans=0; for(i=1;i<=n;i++) A[i]-=i; sort(A+1,A+n+1); for(i=1;i<=n;i++) ans+=abs(A[i]-A[(n+1)/2]); printf("%lld\n",ans); } return 0; }
B题:博弈
思路:假设先手取右上角坐标,如果后手可以赢,那么后手一定有一个必胜状态,但是不管后手的必胜的状态是什么,先手都可以在第一步取到
所以后手除了在n=1和m=1情况下可以赢外,其他情况都输;
#include<stdio.h> int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) puts((n==1&&m==1)?"Bob":"Alice"); return 0; }
C题:
题目链接:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1466
题目描述:
在一棵有向树中, 若节点u至节点v之间有一条有向边, 则我们称u为v的父节点,
也可以称作1祖先节点。所谓节点v的k祖先节点, 指的是节点v的父节点的k-1祖先节点。
现在, 给定你一棵以0为根节点的树, 树上的每个节点都有一个值, 你需要能快速的回
答出从节点v至它的k祖先节点所经过的所有节点中(即v节点、v节点的1祖先、...、v节点
的k-1祖先、v节点的k祖先), 值最大的节点的值的为多少? 其中我们默认0号节点的值为0;
解题思路:
我们构造一个状态pa[i][k]表示i节点的2^k级祖先,dp[i][k]表示i节点到它(2^k)-1级祖先之间
所有祖先的最大值,那么我们更新过程就是:
pa[i][k]=pa[pa[i][k-1]][k-1]; //我的2^k级祖先就是我的2^(k-1)级祖先的2^(k-1)级祖先
dp[i][k]=Max(dp[i][k-1],dp[pa[i][k-1]][k-1]); //理解类似上面
然后就是用一个dfs求出我们最多有多少级祖先,用于判断Wrong request
最后查询一个节点v到它的k级祖先之间的所有祖先的最大值,就是把k+1进行二进制分解后
一步步依次用dp[v][i]来取最大值了。
总的时间复杂度为O(n*logn+q*logn*logn),其实对于后面的那个logn也是可以优化掉的;
#include<math.h> #include<stdlib.h> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; struct E { int t,next; }edge[210000]; int pa[110000][22],dp[110000][22]; int cnt[110000],head[110000],ant; void add(int a,int b) { edge[ant].t=b; edge[ant].next=head[a]; head[a]=ant++; } void dfs(int root,int dep) { int i; cnt[root]=dep; for(i=head[root];i!=-1;i=edge[i].next) dfs(edge[i].t,dep+1); } int fun(int n,int k) { int m=0,i; while(k){ for(i=0;i<=20;i++) if(k<=(1<<i)) break; if(k<(1<<i)) i--; m=max(m,dp[n][i]); k-=(1<<i); n=pa[n][i]; } return m; } int main() { int n,i,k,m; while(scanf("%d",&n)!=EOF) { ant=0; memset(head,-1,sizeof(head)); for(i=1;i<=n;i++) scanf("%d",&dp[i][0]); for(i=1;i<=n;i++){ scanf("%d",&pa[i][0]); add(pa[i][0],i); } for(k=1;k<=20;k++){ for(i=1;i<=n;i++) pa[i][k]=pa[pa[i][k-1]][k-1]; } for(k=1;k<=20;k++){ for(i=1;i<=n;i++) dp[i][k]=max(dp[i][k-1],dp[pa[i][k-1]][k-1]); } dfs(0,0); scanf("%d",&m); while(m--){ scanf("%d%d",&k,&n); if(k>cnt[n]){ puts("Wrong request"); continue; } printf("%d\n",fun(n,k+1)); } } return 0; }
E题:比赛的时候一直用3分来写,一直wa,求告知正确的解法。。。。。。。。。
F题:bfs+优先队列
#include<math.h> #include<stdlib.h> #include<stdio.h> #include<vector> #include<queue> #include<string.h> #include<algorithm> using namespace std; char mat[510][510]; int vis[510][510],dp[510][510],n; int op[4][2]={{1,0},{0,1},{-1,0},{0,-1}}; struct node { int x,y; bool operator<(const node &tem) const{ return dp[x][y]>dp[tem.x][tem.y]; } }; int bfs() { node u,tmp; int i,j; priority_queue <node> que; for(i=1;i<=n;i++) for(j=1;j<=n;j++){ vis[i][j]=0;dp[i][j]=1000000;} vis[1][1]=1;dp[1][1]=0; u.x=u.y=1; que.push(u); while(!que.empty()){ u=que.top();que.pop(); if(u.x==n&&u.y==n) break; for(i=0;i<4;i++){ tmp.x=u.x+op[i][0]; tmp.y=u.y+op[i][1]; if(tmp.x<1||tmp.x>n||tmp.y<1||tmp.y>n) continue; if(dp[u.x][u.y]+mat[tmp.x][tmp.y]-'0'<dp[tmp.x][tmp.y]) { dp[tmp.x][tmp.y]=dp[u.x][u.y]+mat[tmp.x][tmp.y]-'0'; if(!vis[tmp.x][tmp.y]){ vis[tmp.x][tmp.y]=1; que.push(tmp); } } } vis[u.x][u.y]=0; } return dp[n][n]; } int main() { int i; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) scanf("%s",mat[i]+1); printf("%d\n",bfs()); } return 0; }