-----------------------前面的两场感觉质量不高,就没写题解-----------------------------
C .Cent Savings
pro:按顺序给定N个商品,相邻的可以分到同一组,你可以最多分成D+1组,每一组的价格四舍五入,求最小价格。N<2e3,D<20;
sol:由于的相邻的分组,就DP即可,O(N^2*D)。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2010; int dp[maxn][30];//dp[i][j] int a[maxn], sum[maxn]; inline f(int x){return (x / 10 * 10 + (x % 10 <= 4 ? 0 : 10));} int main() { int n, d; cin >> n >> d; for(int i = 1; i <= n; i++)cin >> a[i], sum[i] = a[i] + sum[i - 1]; for(int i = 1; i <= n; i++)dp[i][1] = f(sum[i]); for(int j = 2; j <= d + 1; j++) { for(int i = j; i <= n; i++) { int tmp = 1e9 + 7; for(int k = j - 1; k <= i - 1; k++) { tmp = min(tmp, dp[k][j - 1] + f(sum[i] - sum[k])); } dp[i][j] = tmp; } } int ans = 1e9 + 7; for(int i = 1; i <= d + 1 && i <= n; i++)ans = min(ans, dp[n][i]); cout<<ans<<endl; return 0; }
D .Digi Comp II
pro:给定M个开关,每个开关有初始状态(L或者R),每个开关有两个走向,分别指向左边对应的开关和右边对应的开关。 一个球走到当前开关,会走向当前状态指向的方向,并且使当前的状态改变。 问N个球从1号出发,最终每个开关的状态。 给定的关系是个DAG,除了0号都有两个出度,可以看成左右儿子。
sol:模拟一下,不难发现,如果X个球经过i节点,那么将会有X/2+X&1次走向初始方向,X/2次走向另外一个方向。即是topo排序一下可以搞。
这题WA的蛮多的,有两个注意;
1:不一定只要1号点入度为0,题目只保证了除0之外,出度为2;没保证入度(这一点感觉题目的确误导了很多读题人)。 所以必须开始把入度为0的都加进queue去,不然有的点跑不到。
2:左右儿子一样的情况下,如果先减左右儿子的入度,再入队,会加两次,导致错误。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=1000010; int ind[maxn],ans[maxn]; int a[maxn],b[maxn],q[maxn],head,tail; ll Now[maxn],N; char c[maxn][3]; int main() { int M,u,v; scanf("%lld%d",&N,&M); rep(i,1,M){ scanf("%s%d%d",c[i],&a[i],&b[i]); ind[a[i]]++; ind[b[i]]++; } Now[1]=N; rep(i,1,M) if(!ind[i]) q[++head]=i; while(tail<head){ u=q[++tail]; if(u==0) break; if(Now[u]%2==1) ans[u]=1; if(c[u][0]=='L'){ Now[a[u]]+=(Now[u]-Now[u]/2); Now[b[u]]+=Now[u]/2; } else { Now[b[u]]+=(Now[u]-Now[u]/2); Now[a[u]]+=Now[u]/2; } ind[a[u]]--; if(ind[a[u]]==0) q[++head]=a[u]; ind[b[u]]--; if(ind[b[u]]==0) q[++head]=b[u]; } rep(i,1,M) { if(ans[i]) { if(c[i][0]=='L') putchar('R'); else putchar('L'); } else { if(c[i][0]=='R') putchar('R'); else putchar('L'); } } return 0; }
E :Euclidean TSP
pro:虽然题目是TSP,但是和TSP没什么大关系。 就是给定飞机上升的时间和直线飞行的时间,二者和C的关系,求最小时间。
sol:飞机飞高需要时间,飞高之后飞得快,不难想到总时间随高度先增后减,三分即可。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; double n, p, s, v; const double eps = 1e9; const double t = sqrt(2.0); double f(double x) { double tmp = n * pow(log(n) / log(2.0), t * x); tmp = tmp /(eps * p); tmp = tmp + s / v * (x + 1.0) / x; return tmp; } int main(){ cin >> n >> p >> s >> v; double l =0, r = 100, ans=1e60, c; int T = 100; while(T--) { double lmid = l + (r - l) / 3.0; double rmid = r - (r - l) / 3.0; double Lans=f(lmid),Rans=f(rmid); if(Rans> Lans){ r=rmid; if(ans>Lans) ans =Lans, c = lmid; } else{ l = lmid; if(ans>Rans) ans =Rans, c = rmid; } } printf("%.9f %.9f\n", ans, c); return 0; }
F .Finding Lines
pro:给定N个点,P; 问是否有超过P%的点在一条直线上。
sol:随机两个点,看多少点在直线上。(上海大都会原题)
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; typedef long long ll; const int maxn = 1e5 + 10; struct node { ll x, y; }c[maxn]; node a, b; bool judge(node c) { return (b.y - a.y) * (c.x - b.x) == (b.x - a.x) * (c.y - b.y); } int main() { srand(time(0)); int n, p; cin >> n >> p; for(int i = 1; i <= n; i++)cin >> c[i].x >> c[i].y; if(n <= 2) { cout<<"possible\n"; return 0; } int T = 1000; bool flag = 0; while(T--) { int x = (ll)rand() * rand() % n + 1; int y = (ll)rand() * rand() % n + 1; if(x == y)continue; a = c[x], b= c[y]; int tot = 0; for(int i = 1; i <= n; i++)if(judge(c[i]))tot++; if(1.0 * tot / n + 1e-9 >= 0.01 * p){flag = 1;break;} } if(flag)cout<<"possible"<<endl; else cout<<"impossible"<<endl; return 0; }
G .Gathering
pro:给定二维平面上N个点,以及D。要求找一个点X,满足X到N个点的曼哈顿距离都不超过D,而总距离最小。 不存在合法的输出"impossible"
sol:因为我们知道中位数这个位置比较关键,X和Y可以分开考虑,分别是取中位数时最小,对X和Y都是凹函数。
比赛的时候大方向想对了。大概是曼哈顿距离转化切比雪夫,然后套二分或者三分。 (但是居然求公共矩形不会了。。。赛后想不就是对R和U取min,对取L和U取max吗。。。 然后我们在合法的多边形里三分套三分即可。 也可以三分之后直接找最接近中位数的Y即可。
曼哈顿转切比雪夫: 原点A(X,Y)---->逆时针旋转45度后,长度*sqrt2的点A‘(X+Y,X-Y);
切比雪夫转曼哈顿: L<=x<=R ; D<=y<=U ; 现在:(L+D)/2<=x<=(R+U)/2,且x一定时满足,L<=x+y<=R,D<=y<=U;
三分套三分:O(N(log1e9)^2~=9e7),会超时T37.
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; const ll inf=1e30; ll x[maxn],y[maxn],D,ans=1e30,midy; ll up=inf,dn=-inf,le=-inf,ri=inf; int N; ll solvey(ll midx,ll Y) { ll res=0; rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]); return res; } ll solvex(ll midx) { ll yu=min(midx-dn,ri-midx); ll yd=max(midx-up,le-midx); if(yu<yd) return inf; ll res=inf,mid; while(yd<yu-2){ ll l=yd+(yu-yd)/3,ans1=solvey(midx,l); ll r=yu-(yu-yd)/3,ans2=solvey(midx,r); if(ans1>ans2) yd=l,res=min(res,ans2); else yu=r,res=min(res,ans1); } rep(i,yd,yu) res=min(res,solvey(midx,i)); return res; } int main() { scanf("%d",&N); rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]); scanf("%lld",&D); rep(i,1,N){ dn=max(dn,x[i]-y[i]-D); up=min(up,x[i]-y[i]+D); le=max(le,x[i]+y[i]-D); ri=min(ri,x[i]+y[i]+D); } if(dn>up||le>ri) return puts("impossible"),0; sort(y+1,y+N+1); midy=(y[(N+1)/2]); ll L=(le+dn)/2,R=(up+ri)/2; while(L<R-2){ ll l=L+(R-L)/3,ans1=solvex(l); ll r=R-(R-L)/3,ans2=solvex(r); if(ans1>ans2) L=l,ans=min(ans,ans2); else R=r,ans=min(ans,ans1); } rep(i,L,R) ans=min(ans,solvex(i)); printf("%lld\n",ans); return 0; }
三分+中位数逼近:没必要取三分y,直接用结论即可,取靠近y[]的中位数。
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=100010; const ll inf=1e30; ll x[maxn],y[maxn],D,ans=1e30,midy; ll up=inf,dn=-inf,le=-inf,ri=inf; int N; ll solve(ll midx) { ll yu=min(midx-dn,ri-midx); ll yd=max(midx-up,le-midx); if(yu<yd) return inf; ll Y,res=0; rep(i,1,N) res+=abs(midx-x[i])+abs(Y-y[i]); return res; } int main() { scanf("%d",&N); rep(i,1,N) scanf("%lld%lld",&x[i],&y[i]); scanf("%lld",&D); rep(i,1,N){ dn=max(dn,x[i]-y[i]-D); up=min(up,x[i]-y[i]+D); le=max(le,x[i]+y[i]-D); ri=min(ri,x[i]+y[i]+D); } if(dn>up||le>ri) return puts("impossible"),0; sort(y+1,y+N+1); midy=(y[(N+1)/2]); ll L=(le+dn)/2,R=(up+ri)/2; while(L<R-2){ ll l=L+(R-L)/3,ans1=solve(l); ll r=R-(R-L)/3,ans2=solve(r); if(ans1>ans2) L=l,ans=min(ans,ans2); else R=r,ans=min(ans,ans1); } rep(i,L,R) ans=min(ans,solve(i)); printf("%lld\n",ans); return 0; }
H .Hyacinth
pro:看不懂。
sol:by队友
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; struct edge{ int d,nex; }e[20010]; struct node{ int x,y; bool a,b; }a[10010]; int q[10010],efree,n,cnt; inline void add(int x,int y){e[++efree]=(edge){y,q[x]},q[x]=efree;} void dfs(int x,int y){ if(a[x].a==0)a[x].x=++cnt; if(a[x].b==0)a[x].y=++cnt; int v=q[x];while(e[v].d==y)v=e[v].nex; if(!v)return; if(a[x].a)a[e[v].d].x=a[x].y,a[e[v].d].a=a[x].b=1; else a[e[v].d].x=a[x].x,a[e[v].d].a=a[x].a=1; dfs(e[v].d,x); for(int i=e[v].nex;i;i=e[i].nex) if(e[i].d!=y){ if(a[x].a)a[e[i].d].x=a[x].y,a[e[i].d].a=a[x].b=1; else a[e[i].d].x=a[x].x,a[e[i].d].a=a[x].a=1; dfs(e[i].d,x); } } int main(){ scanf("%d",&n); if(n==2){ puts("1 2"); puts("2 1"); return 0; } for(int i=1;i<n;i++){ int x,y;scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,0); for(int i=1;i<=n;i++)printf("%d %d\n",a[i].x,a[i].y); return 0; }
J .Judging Troubles
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; map<string, int>Map1, Map2; int main() { int n, ans = 0; string s; cin >> n; for(int i = 1; i <= n; i++)cin >> s, Map1[s]++; for(int i = 1; i <= n; i++)cin >> s, Map2[s]++; for(map<string, int>::iterator it = Map1.begin(); it != Map1.end(); it++) ans += min(Map1[it->first], Map2[it->first]); cout<<ans<<endl; return 0; }