Gym101485: NWERC 2015(队内第6次训练)
A .Assigning Workstations
题意:给定N个人的工作时间和工作时长,我们可以假设有无数台工作机器,如果一台机器超过M时间未使用就会关闭,那么我们怎么安排机器的使用,使得需要开启机器的次数最少。
思路:贪心,维护一个时间队列q,维护一个单调队列q1; 前者表示没有使用了但还没关闭的机器队列,后者表示还在使用的机器。 那么我们每次把已经使用完的加入q,如果关闭了或者被新的人使用了就移除。 使用的新机器加入q1....
#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 = 3e5 + 10; struct node { ll a, s; bool operator < (const node& b)const{ return a < b.a || a == b.a && s < b.s; } }a[maxn]; int ans; ll n, m; queue<int>q; priority_queue<int>q1; int main() { scanf("%lld%lld", &n, &m); for(int i = 1; i <= n; i++) scanf("%lld%lld", &a[i].a, &a[i].s); sort(a + 1, a + 1 + n); for(int i = 1; i <= n; i++) { while(!q1.empty()) if(-q1.top() <= a[i].a)q.push(-q1.top()+m),q1.pop(); else break; while(!q.empty()&&q.front()<a[i].a)q.pop(); if(!q.empty())q1.push(-a[i].a-a[i].s),q.pop(); else ans++,q1.push(-a[i].a-a[i].s); } printf("%d",n-ans); return 0; }
B .Better Productivity
题意:给出N个人,现在让你分P组,每组的工作效率是最小结束时间-最大开始时间,要求每一组的效率的正数,求最大效率和。
思路: 把包含至少一个其他的分到A组;否则到B组。
A组的要么单独分到一组,要么和它包含的某一个在一组(可以反证,假设已经分好组了,现在把不是单独分组的A加进去,如果分到不是包含关系的里面去,只会把答案变小)。
分组可以用栈进行。 而不是N^2枚举,因为多个相同的时候我们可以要保留一个作为最小的一个分到B组。
然后,现在A里面的没有包含关系了,我们可以排序,排序后一定是相邻的分到同一组,这里DP即可。
枚举单独分组的A,加上dp[][]跟新最大值即可。 dp[i][j]表示前i个人分j组的最大效率和; mp[i][j]表示i到j的人分一组的效率。
(这种题一看就是排序,毕竟交集肯定是相邻的在一起最大,然后DP或者线段树啥的。但是这里按照左端点和右端点呢,显然在有包含关系的时候都不行,所以要去掉包含关系,比赛的时候想到这里了,然后就没思路了。 关键在于想到A组的人如果不单独分组的话,他可以分到合理的组,使得答案不减小。
#include<bits/stdc++.h> #define pii pair<int,int> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=210; pii a[maxn];int tot,cnt,ans; int vis[maxn],dp[maxn][maxn],len[maxn],mp[maxn][maxn]; int main() { int N,P; scanf("%d%d",&N,&P); rep(i,1,N) scanf("%d%d",&a[i].first,&a[i].second); sort(a+1,a+N+1); rep(i,1,N) rep(j,i+1,N) if(a[i].second>=a[j].second){ vis[i]=1; break;} rep(i,1,N) if(vis[i]) len[++tot]=a[i].second-a[i].first; else a[++cnt]=a[i]; sort(len+1,len+tot+1); reverse(len+1,len+tot+1); rep(i,1,tot) len[i]+=len[i-1]; rep(i,1,cnt){ int Mx=a[i].second,Mn=a[i].first; rep(j,i,cnt){ Mx=min(Mx,a[j].second),Mn=max(Mn,a[j].first); mp[i][j]=Mx-Mn; } } memset(dp,-1,sizeof(dp)); dp[0][0]=0; rep(i,1,min(cnt,P)) rep(j,1,cnt) rep(k,0,j-1) { if(mp[k+1][j]>0&&dp[k][i-1]!=-1) dp[j][i]=max(dp[j][i],dp[k][i-1]+mp[k+1][j]); } rep(i,max(P-cnt,0),tot){ if(dp[cnt][P-i]>0) ans=max(ans,len[i]+dp[cnt][P-i]); } printf("%d\n",ans); return 0; }
C .Cleaning Pipes
#include<bits/stdc++.h> #define pii pair<int,int> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const double eps = 1e-10; const double pi = 3.1415926535897 ; struct Point { double x, y; Point(double x = 0, double y = 0):x(x), y(y){} }; typedef Point Vector; Vector operator + (Vector A, Vector B){return Vector(A.x+B.x, A.y+B.y);}//向量+向量=向量;点+向量=向量 Vector operator - (Vector A, Vector B){return Vector(A.x-B.x, A.y-B.y);}//点-点=向量 bool operator < (const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); } int dcmp(double x)//三态函数,高精度判断 { if(fabs(x) < eps)return 0; else return x < 0 ? -1 : 1; } bool operator == (const Point& a, const Point& b) { return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0; } double Cross(Vector A, Vector B){return A.x*B.y - A.y*B.x;}//叉积 bool SegmentProperIntersection(Point a1, Point a2, Point b1, Point b2) //判断线段a1a2和b1b2是否规范相交(在端点处相交得用下一个函数特殊判断) { double c1 = Cross(a2-a1, b1-a1), c2 = Cross(a2-a1, b2-a1), c3 = Cross(b2-b1, a1-b1), c4 = Cross(b2-b1, a2-b1); return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0; } Point a[1010]; int c[1010]; Point b[1010]; bool judge(int i, int j)//如果i和j相交,且交点不为两个线段的起点 { if(b[i] == b[j])return true; if(a[c[i]] == a[c[j]] || a[c[i]] == b[j] || b[i] == a[c[j]])return false; return SegmentProperIntersection(a[c[i]],b[i],a[c[j]],b[j]); } int color[1010];//0表示未染色 1表示白色 2表示黑色 vector<int>G[1010]; bool bipartite(int u) { for(int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if(color[u] == color[v])return false;//u v颜色一样 if(!color[v]) { color[v] = 3 - color[u];//节点u与v染不同的颜色 if(!bipartite(v))return false; } } return true; } int main(){ int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i++)scanf("%lf%lf", &a[i].x, &a[i].y); for(int i = 1; i <= m; i++) scanf("%d%lf%lf", &c[i], &b[i].x, &b[i].y); for(int i = 1; i <= m; i++) for(int j = i + 1; j <= m; j++) if(judge(i, j))G[i].push_back(j),G[j].push_back(i); bool flag = 0; for(int i = 1; i <= m; i++)if(!color[i]) { color[i] = 1; if(!bipartite(i)){flag = 1;break;} } if(flag)puts("impossible"); else puts("possible"); return 0; }
D .Debugging
题意:题目给N,R,P。表示给出一个一定有一行bug的代码,长度为N,现在让你debug,你可以在某处加一行“输出”,时间是P; 运行一次的时间是R。(输出是表示你可以对比这里的放的答案是否符合预期)。现在稳你用最有策略,最坏情况下的时间。 N<1e6; R,P<1e9
思路:N很小,所以所有长度的答案都可以算出来,估计就是记忆化了。一开始以为要么是二分,要么用L-1行输出。WA了。然后才去想,还是应该把所有的情况都求出来,长度为L的时候 ,res=min((L+i)/i+R+i*P);我们整数分块即可。
题目的最坏情况表示:你下一次需要去检验的长度是当前最长的一个区间块,即向上取整,所以我们用(L+i)/i
#include<bits/stdc++.h> #define pii pair<int,int> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=210; pii a[maxn];int tot,cnt,ans; int vis[maxn],dp[maxn][maxn],len[maxn],mp[maxn][maxn]; int main() { int N,P; scanf("%d%d",&N,&P); rep(i,1,N) scanf("%d%d",&a[i].first,&a[i].second); sort(a+1,a+N+1); rep(i,1,N) rep(j,i+1,N) if(a[i].second>=a[j].second){ vis[i]=1; break;} rep(i,1,N) if(vis[i]) len[++tot]=a[i].second-a[i].first; else a[++cnt]=a[i]; sort(len+1,len+tot+1); reverse(len+1,len+tot+1); rep(i,1,tot) len[i]+=len[i-1]; rep(i,1,cnt){ int Mx=a[i].second,Mn=a[i].first; rep(j,i,cnt){ Mx=min(Mx,a[j].second),Mn=max(Mn,a[j].first); mp[i][j]=Mx-Mn; } } memset(dp,-1,sizeof(dp)); dp[0][0]=0; rep(i,1,min(cnt,P)) rep(j,1,cnt) rep(k,0,j-1) { if(mp[k+1][j]>0&&dp[k][i-1]!=-1) dp[j][i]=max(dp[j][i],dp[k][i-1]+mp[k+1][j]); } rep(i,max(P-cnt,0),tot){ if(dp[cnt][P-i]>0) ans=max(ans,len[i]+dp[cnt][P-i]); } printf("%d\n",ans); return 0; }
E .Elementary Math
题意:给你一个数 N, 然后下面有 N 对数,每对数可以进行 + - * 操作,每对数选择一个结果,是不是存在每对数选择的结果都不一样。如果是,把每对数选择的结果写下来,并记录下是什么操作,如果不行,就输出 impossible。
思路:这个题看着就是一个二分图匹配问题,看能否匹配N对。
#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,dat; }e[1000000]; int n,q[10010],d[10010],efree=1,cnt,ans,fh[2510]; long long x[2510],y[2510],num[10010];char f[5]; queue<int>que; map<long long,int>ma; map<long long,int>::iterator ii; inline void add(int x,int y,int z){e[++efree]=(edge){y,q[x],z};q[x]=efree;} int dfs(int x,int y){ if(x==cnt+1||!y)return y; int now,tot=0; for(int i=q[x];i;i=e[i].nex) if(d[e[i].d]==d[x]+1&&e[i].dat){ now=dfs(e[i].d,min(y,e[i].dat)); e[i].dat-=now;e[i^1].dat+=now; tot+=now;if(tot==y)return y; } if(!tot)d[x]=-1; return tot; } inline bool bfs(){ memset(d,0,sizeof(d));d[0]=1; que.push(0); while(!que.empty()){ int x=que.front();que.pop(); for(int i=q[x];i;i=e[i].nex) if(e[i].dat&&!d[e[i].d])d[e[i].d]=d[x]+1,que.push(e[i].d); } return d[cnt+1]; } void dfs(int xx){ for(int i=q[xx];i;i=e[i].nex) if(e[i].dat&&e[i].d<=n){ if(x[e[i].d]+y[e[i].d]==num[xx])fh[e[i].d]=1; else if(x[e[i].d]-y[e[i].d]==num[xx])fh[e[i].d]=2; else fh[e[i].d]=3; return; } } long long ff(long long x,long long y,int z){ if(z==1)return x+y; if(z==2)return x-y; return x*y; } int main(){ scanf("%d",&n);cnt=n;f[1]='+';f[2]='-';f[3]='*'; for(int i=1;i<=n;i++){ add(0,i,1),add(i,0,0); scanf("%lld%lld",x+i,y+i); long long a=x[i],b=y[i]; if(!ma[a+b])ma[a+b]=++cnt,num[cnt]=a+b; if(!ma[a-b])ma[a-b]=++cnt,num[cnt]=a-b; if(!ma[a*b])ma[a*b]=++cnt,num[cnt]=a*b; add(i,ma[a+b],1),add(ma[a+b],i,0); add(i,ma[a*b],1),add(ma[a*b],i,0); add(i,ma[a-b],1),add(ma[a-b],i,0); } for(ii=ma.begin();ii!=ma.end();ii++) add((*ii).second,cnt+1,1),add(cnt+1,(*ii).second,0); while(bfs())ans+=dfs(0,2147483647); if(ans==n){ for(int i=q[cnt+1];i;i=e[i].nex) if(e[i].dat)dfs(e[i].d); for(int i=1;i<=n;i++)printf("%lld %c %lld = %lld\n",x[i],f[fh[i]],y[i],ff(x[i],y[i],fh[i])); } else puts("impossible"); return 0; }
G .Guessing Camels
题意:给定三个N的排列A,B,C;问多少对数字(x,y),在三个排列里x出现的位置在y的前面
思路:BC按照A标号,然后CDQ分治即可。(这里可以把排序的部分转化为双指针,会快一个log)
#include<bits/stdc++.h> #define pii pair<int,int> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=4000010; int a[maxn],b[maxn],c[maxn],pos[maxn]; ll sum[maxn],ans; int N; struct in{ int id,x,y; }s[maxn]; bool cmp1(in w,in v){ return w.id<v.id;} bool cmp2(in w,in v){ return w.x<v.x; } void add(int x,int val) { while(x<=N){ sum[x]+=val;x+=(-x)&x; } } int query(int x){ int res=0; while(x){ res+=sum[x]; x-=(-x)&x; } return res; } void solve(int L,int R) { if(L>=R) return ; int Mid=(L+R)>>1; solve(L,Mid); sort(s+L,s+R+1,cmp2); rep(i,L,R){ if(s[i].id<=Mid) add(s[i].y,1); else ans+=query(s[i].y); } rep(i,L,R){ if(s[i].id<=Mid) add(s[i].y,-1); } sort(s+L,s+R+1,cmp1); solve(Mid+1,R); } int main() { scanf("%d",&N); rep(i,1,N) scanf("%d",&a[i]),pos[a[i]]=i; rep(i,1,N) scanf("%d",&b[i]),b[i]=pos[b[i]],s[b[i]].x=i; rep(i,1,N) scanf("%d",&c[i]),c[i]=pos[c[i]],s[c[i]].y=i; rep(i,1,N) s[i].id=i; solve(1,N); printf("%lld\n",ans); return 0; }
I .Identifying Map Tiles
递推找位置:
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int dir[][2] = {0,0,1,0,0,1,1,1}; ll f[40]; int main() { string s; cin >> s; ll n = s.size(); ll x = dir[s[0] - '0'][0]; ll y = dir[s[0] - '0'][1]; x++,y++; for(int i = 1; i < n; i++) { x = 2 * x - 1, y = 2 * y - 1; if(s[i] == '1')x++; else if(s[i] == '2')y++; else if(s[i] == '3')x++,y++; } x--,y--; cout<<n<<" "<<x<<" "<<y<<endl; return 0; }
J .Jumbled Communication
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int ans[1000]; int main() { for(int i = 0; i <= 255; i++)ans[((i<<1)&255) ^ i] = i; int n; scanf("%d", &n); for(int i = 0; i < n; i++) { int x; scanf("%d", &x); printf("%d", ans[x]); if(i == n - 1)puts(""); else putchar(' '); } return 0; }
K .Kitchen Combinatorics
题意: 从前菜,主菜,点心中各选一道菜,每道菜由一些原料组成,每种原料有一些不同的品种,问总共有多少种方法组成这三道菜。如果多道菜使用了相同的原料,则这些菜中的这种原料将是相同品种的,另外有一些不可以组合在一起的菜。
思路: 枚举所有可能组合,统计该组合使用不同原料的数目,将原料品种数相乘即为该组合方案数。相乘前先要用除法判断是否超过1e18
#include<bits/stdc++.h> #define ll long long #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; int r,s,m,d,n,k[100],b[1010],a[100][25]; bool f[100][100],fl[1010];long long ans; int main(){ scanf("%d%d%d%d%d",&r,&s,&m,&d,&n); for(int i=1;i<=r;i++)scanf("%d",b+i); for(int i=1;i<=s+m+d;i++){ scanf("%d",k+i); for(int j=1;j<=k[i];j++)scanf("%d",a[i]+j); } for(int i=1;i<=n;i++){ int x,y; scanf("%d%d",&x,&y); f[x][y]=f[y][x]=1; } for(int i=1;i<=s;i++) for(int j=1;j<=m;j++) if(!f[i][s+j]) for(int l=1;l<=d;l++) if(!f[i][l+s+m]&&!f[s+j][l+s+m]){ memset(fl,0,sizeof(fl)); for(int p=1;p<=k[i];p++)fl[a[i][p]]=1; for(int p=1;p<=k[j+s];p++)fl[a[j+s][p]]=1; for(int p=1;p<=k[l+s+m];p++)fl[a[l+s+m][p]]=1; long long now=1;bool flag=0; for(int p=1;p<=r;p++) if(fl[p]){ if(now>1e18/b[p])return 0*puts("too many"); now*=b[p]; } if(ans+now>1e18)return 0*puts("too many"); ans+=now; } printf("%lld",ans); return 0; }