Social Infrastructure Information Systems Division, Hitachi Programming Contest 2020
A - Hitachi String
#include <bits/stdc++.h> #define ll long long using namespace std; char s[15]; int main() { //freopen("in.txt","r",stdin); scanf("%s",s); int n=strlen(s); if(n&1) printf("No\n"); else { bool f=true; for(int i=0;i<n;i+=2) { if(s[i]!='h'||s[i+1]!='i') f=false; } printf("%s\n",f?"Yes":"No"); } return 0; }
B - Nice Shopping
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=1e5+5; int a[N],b[N]; int main() { //freopen("in.txt","r",stdin); int n,m,q,mi=1e9,ans=1e9; scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); mi=min(mi,a[i]); } for(int i=1;i<=m;i++) { scanf("%d",&b[i]); ans=min(ans,mi+b[i]); } for(int i=0,x,y,c;i<q;i++) { scanf("%d%d%d",&x,&y,&c); ans=min(ans,a[x]+b[y]-c); } printf("%d\n",ans); return 0; }
C - ThREE
题意:给一个有N个节点的无根树,要求将1~N分别给N个结点当作权值,且满足任意一对距离为3的节点权值的和或积是3的倍数,若不存在输出-1。
数据范围:$2 \leq N \leq 2 \times 10^{5}$
题解:可以发现只有两个模3为1的数和两个模3为2的数的情况,不满足和或积是3的倍数。以1为根,求出每个点的深度,距离为3的两个节点深度差为3或1,因此可以根据深度奇偶性来分配模为1和模为2的数。
这里记深度为奇数的个数为x,深度为偶数的个数为y。
1.$x\geq \frac{N}{3} \ and \ y\geq \frac{N}{3}$
将模为1的数依次分配给深度为奇数的点,将模为2的数依次分配给偶数深度的点,剩余的数随便分配。
2.$x \leq \frac{N}{3}$
将模为0的数以此分配给深度为奇数的点,其它数分配给深度为偶数的点。
3.$y \leq \frac{N}{3}$
将模为0的数以此分配给深度为偶数的点,其它数分配给深度为奇数的点。
以上包含所有情况,因此不存在的情况没有。
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=2e5+5; vector<int> G[N]; int dep[N],ans[N]; void dfs(int u,int fa) { for(auto v:G[u]) { if(v==fa) continue; dep[v]=dep[u]+1; dfs(v,u); } } int main() { //freopen("in.txt","r",stdin); int n; scanf("%d",&n); for(int i=1,u,v;i<n;i++) { scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } dfs(1,-1); vector<int> A,B,V[3],Ans[2]; for(int i=1;i<=n;i++) { if(dep[i]&1) A.push_back(i); else B.push_back(i); } for(int i=1;i<=n;i++) { V[i%3].push_back(i); } if(A.size()>=V[1].size()&&B.size()>=V[2].size()) { for(auto it:V[1]) Ans[0].push_back(it); for(auto it:V[2]) Ans[1].push_back(it); int t=A.size()-V[1].size(); for(int i=0;i<t;i++) Ans[0].push_back(V[0][i]); for(int i=t;i<V[0].size();i++) Ans[1].push_back(V[0][i]); } else if(A.size()<V[1].size()) { for(int i=0;i<A.size();i++) Ans[0].push_back(V[0][i]); for(int i=A.size();i<V[0].size();i++) Ans[1].push_back(V[0][i]); for(auto it:V[1]) Ans[1].push_back(it); for(auto it:V[2]) Ans[1].push_back(it); } else { for(int i=0;i<B.size();i++) Ans[1].push_back(V[0][i]); for(int i=B.size();i<V[0].size();i++) Ans[0].push_back(V[0][i]); for(auto it:V[1]) Ans[0].push_back(it); for(auto it:V[2]) Ans[0].push_back(it); } for(int i=0;i<A.size();i++) { ans[A[i]]=Ans[0][i]; } for(int i=0;i<B.size();i++) { ans[B[i]]=Ans[1][i]; } for(int i=1;i<=n;i++) { printf("%d%c",ans[i],i==n?'\n':' '); } return 0; }
D - Manga Market
题意:有N个商店,当到达第i个商店的时间为t,则在该商店排队ai*t+bi时间单位,问T个时间单位最多能到多少个商店买东西(一个商店只能买一次)。
数据范围:$1 \leq N \leq 2\times 10^{5},0 \leq ai,bi,T \leq 10^{9}$
题解:考虑当前时刻去哪个商店更优,设t时刻准备去第i个商店比第j个商店更优,可以得出:
$1+a_{i}\times(t+1)+b_{i}+1+a_{j}\times(t+1+a_{i}\times(t+1)+b_{i}+1)+b_{j} \leq1+a_{j}\times(t+1)+b_{j}+1+a_{i}\times(t+1+a_{j}\times(t+1)+b_{j}+1)+b_{i}\Leftrightarrow \frac{a_{j}}{b_{j}+1} \geq \frac{a_{i}}{b_{i}+1}$
将N个商店按$\frac{a}{b+1}$降序,然后进行动态规划,按最暴力的需要O(N^2)。
1.当a=0时,显然按b从小到大取。
2.当a!=0时,可以发现时间是指数级上升的,显然能去的商店不超过30个。
定义f[i][j]代表1~i个商店里去了j个商店后的最少时间,然后二分判断把a=0的商店加进去,更新答案即可。
#include <bits/stdc++.h> #define ll long long using namespace std; const int N=2e5+5; struct P { int a,b; bool operator <(const P &t) const { return a*(t.b+1)>t.a*(b+1); } }p[N]; ll pre[N]; int n,t,cnt=0,tot=0; int cal() { ll f[35]; for(int i=0;i<35;i++) f[i]=(i?1e9:0); for(int i=0;i<cnt;i++) { for(int j=30;j>=0;j--) { if(f[j]==1e9) continue; f[j+1]=min(f[j+1],(f[j]+1)*(p[i].a+1)+p[i].b); if(f[j+1]>t) f[j+1]=1e9; } } int ans=0; for(int i=0;i<32;i++) { if(f[i]!=1e9) { int x=t-f[i]; int l=1,r=tot,y=-1; while(l<=r) { int mid=(l+r)>>1; if(pre[mid]<=x) y=mid,l=mid+1; else r=mid-1; } if(y!=-1) ans=max(ans,i+y); else ans=max(ans,i); } } return ans; } int main() { //freopen("in.txt","r",stdin); scanf("%d%d",&n,&t); for(int i=0,a,b;i<n;i++) { scanf("%d%d",&a,&b); if(a==0) pre[++tot]=b; else p[cnt++]={a,b}; } sort(p,p+cnt),sort(pre+1,pre+tot+1); for(int i=1;i<=tot;i++) pre[i]+=pre[i-1]+1; printf("%d\n",cal()); return 0; }