Codeforces Round #536 (Div. 2)
A. Lunar New Year and Cross Counting
水
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=510; int n; char s[maxn][maxn]; int cnt; int main(){ while(cin>>n) { for(int i=1;i<=n;i++){ scanf("%s",s[i]+1); } cnt=0; for(int i=2;i<n;i++) { for(int j=2;j<n;j++) { if(s[i-1][j-1]=='X'&&s[i+1][j-1]=='X'&&s[i][j]=='X'&&s[i-1][j+1]=='X'&&s[i+1][j+1]=='X')cnt++; } } cout<<cnt<<endl; } }
B. Lunar New Year and Food Ordering
题意:(翻译来自洛谷)
思路:
如果在一个菜品够买的情况下,当然是直接买,但是如果不够的话,就需要按照一定的优先级来维护这些物品了,显然是优先队列,优先队列中的元素只存储id和cost,然后剩余的量用一个r数组来保存,如果从优先队列里pop出来的东西剩余量为0就直接扔掉。
B题居然会这么麻烦。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; int n,m; struct node{ int id; ll cost; friend bool operator<(const node &a,const node &b){ if(a.cost-b.cost)return a.cost>b.cost; return a.id>b.id; } }a[maxn]; priority_queue<node >q; ll r[maxn]; ll ans[maxn]; int main(){ while(cin>>n>>m){ clr(ans,0); while(!q.empty())q.pop(); for(int i=1;i<=n;i++) { scanf("%lld",&r[i]); } for(int i=1;i<=n;i++) { scanf("%lld",&a[i].cost); a[i].id=i; q.push(a[i]); } for(int i=1;i<=m;i++) { int t; ll d; scanf("%d%lld",&t,&d); ans[i]+=min(d,r[t])*a[t].cost; ll v=min(d,r[t]); d-=v; r[t]-=v; while(d>0){ if(q.empty())break; node s=q.top(); if(r[s.id]==0){ q.pop(); continue; } v=min(d,r[s.id]); ans[i]+=v*a[s.id].cost; d-=v; r[s.id]-=v; } if(d>0){ ans[i]=0; } } for(int i=1;i<=m;i++) { printf("%lld\n",ans[i]); } } }
C. Lunar New Year and Number Division
思路:可以证明(等会证明)要两两分组,并且从小到大排,然后i 和n-i+1位置结合是最小的。
上面结论的两部分,后面一部分很好证明,个人认为前面的一部分有点难证明,其他博客的证明对这个两两分组最优讲的都不是很详细,某天在群里和大佬讨论后,大佬证明两两结合比三三结合更优。
这里面a是两个数字相加,d也是两个数字的相加,所以就是两两组合和三三组合。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=300010; int n; ll a[maxn]; ll ans; int main(){ while(cin>>n) { ans=0; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); } sort(a+1,a+1+n); for(int i=1;i<=n/2;i++) { ans+=(a[i]+a[n-i+1])*(a[i]+a[n-i+1]); } cout<<ans<<endl; } }
D. Lunar New Year and a Wander
题意:在一个连通图上从1出发,每次到一个没有到达过的城市,记录标号,城市可以来回走,求字典序最小的路径。
思路:每到一个新城市,就把这个城市可以到达的城市标号塞入一个优先队列,优先弹出最小的标号即可。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; vector<int >ve[maxn]; bool vis[maxn]; int n,m,u,v; int ans[maxn],tot; void init(){ for(int i=1;i<=n;i++) { ve[i].clear(); } clr(vis,0); tot=0; } priority_queue<int >q; int main(){ while(cin>>n>>m) { init(); while(m--) { scanf("%d%d",&u,&v); ve[u].push_back(v); ve[v].push_back(u); } q.push(-1); vis[1]=1; while(tot<n) { int u=-q.top(); q.pop(); ans[++tot]=u; for(auto it:ve[u]){ if(vis[it])continue; vis[it]=1; q.push(-it); } } for(int i=1;i<=tot;i++) { printf("%d%c",ans[i]," \n"[i==tot]); } } }
E. Lunar New Year and Red Envelopes
题意:翻译来自洛谷
思路:
将红包按价格从小打到排序,相同的按d从小到大排序,然后依次用线段树来更新,就可以得到在任意一秒如果抢红包,抢的是哪个红包,由于这个排序,所以如果后面的红包把前面的红包覆盖掉了,也是优先级高的覆盖了优先级低的。
然后就是dp了,如果不考虑d的话,我们从前往后用一个二维dp就很好做了,但是现在这个d存在,要怎么办呢,我们倒着dp,并且转移的时候 $ f[i][j]=min(f[i+1][j-1],f[a[x].d+1][j]+a[x].w) $ x就是在第i秒抢到的红包标号。
由于s<t<d,所以这个dp保证了一个红包不会被抢两次。
吐槽自己,居然想用普通的树状数组完成区间改值,单点查询的操作,蠢的一批。。
#include<bits/stdc++.h> #define clr(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long ll; const int maxn=100010; int n,m,k; ll c[maxn]; ll f[maxn][210]; struct node{ int s,t,d; ll w; friend bool operator<(const node &a,const node &b) { if(a.w!=b.w)return a.w<b.w;; return a.d<b.d; } }a[maxn]; ll num[maxn]; struct segNode{ ll sum,lazy; }tr[maxn<<2]; void build(int o,int l,int r){ if(l==r){ tr[o].sum=0; tr[o].lazy=-1; return ; } int mid=(l+r)>>1; build(o<<1,l,mid); build((o<<1)|1,mid+1,r); tr[o].sum=tr[o<<1].sum+tr[(o<<1)|1].sum; tr[o].lazy=-1; } void pushup(int o){ tr[o].sum=tr[o<<1].sum+tr[o<<1|1].sum; } void pushdown(int o,int l,int r){ if(tr[o].lazy!=-1){ tr[o<<1].lazy=tr[o].lazy; tr[o<<1|1].lazy=tr[o].lazy; int mid=(l+r)>>1; tr[o<<1].sum=tr[o].lazy*(mid-l+1); tr[o<<1|1].sum=tr[o].lazy*(r-mid); tr[o].lazy=-1; } } void update(int o,int l,int r,int ql,int qr,ll val){ if(ql<=l&&r<=qr){ tr[o].lazy=val; tr[o].sum=val*(r-l+1); return; } pushdown(o,l,r); int mid=l+((r-l)>>1); if(ql<=mid)update(o<<1,l,mid,ql,qr,val); if(qr>=mid+1)update(o<<1|1,mid+1,r,ql,qr,val); pushup(o); } ll query(int o,int l,int r,int ql,int qr){ if(ql<=l&&qr>=r)return tr[o].sum; pushdown(o,l,r); int mid=(l+r)>>1; ll ans=0; if(ql<=mid)ans=query(o<<1,l,mid,ql,qr); if(qr>=mid+1)ans+=query(o<<1|1,mid+1,r,ql,qr); return ans; } int main(){ while(cin>>n>>m>>k) { clr(c,0); clr(num,0); clr(f,0); for(int i=1;i<=k;i++) { scanf("%d%d%d%lld",&a[i].s,&a[i].t,&a[i].d,&a[i].w); } sort(a+1,a+1+k); build(1,1,n); for(int i=1;i<=k;i++) { update(1,1,n,a[i].s,a[i].t,(ll)i); } for(int i=1;i<=n;i++) { num[i]=query(1,1,n,i,i); } for(int i=n;i>0;i--) { if(num[i]==0) { for(int j=0;j<=m;j++) { f[i][j]=f[i+1][j]; } continue; } int x=num[i]; f[i][0]=f[a[x].d+1][0]+a[x].w; for(int j=1;j<=m;j++) { f[i][j]=min(f[i+1][j-1],f[a[x].d+1][j]+a[x].w); } } printf("%lld\n",f[1][m]); } }