2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) 体验记
Posted zhouzhendong
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) 体验记相关的知识,希望对你有一定的参考价值。
原文链接https://www.cnblogs.com/zhouzhendong/p/CF1070.html
比赛网址:http://codeforces.com/contest/1070
感受
本来只打算玩玩的。
结果玩的海星。
我做 A 题的时候可能比较浮躁吧,各种傻错误,爆了 7 个罚时。
我 A 还没过,cly 神仙已经把 CDK 切光了。
4点半过一会儿,各自回家。cly 要剃头,就咕咕咕了。我本来也要剃的,后来临时决定先打题。
然后过了 A ,顺手切掉了 H 和 F ,开了 E ,猜它是个凸函数,果断 三分,结果 wa 了。
这个时候刚好 cly 咕回来了。一眼就说不是凸的,tql 。然后我仔细想想发现的确不是,但还是还是可做的,把我代码稍加修改就过了。
cly 看了一波 I 说(显然是装的)他不大会。经过一波讨论之后,发现一个网络流做法。然后 cly 把锅给了我。
谁知道不好的事情发生了:我一开始想的建图是萎的。直到后来,换了建图方式才过。在此期间,cly 干掉了 J 和 G,tql 。
然后一起肝 B 。这个题目好**长。也许只有 AK英语 的 cly 才能读懂了。十几分钟过去,cly 告诉了我题意。
这……这不是傻题吗?直接建个 Trie 就没了啊!为什么过的人这么少??????要来不及了!快快快!于是开始码呀码……
结果我离 AC 差了 4 分钟。
然后就只有 rk26 啦。
没事反正是玩玩。
部分题目 做法概要 或 代码
A 简单题——cly 0.1s 想出做法,估计 1s 就可以 AC 的题。 直接两维状态,一维是对于 d 取模的值,一维是数位和。直接 bfs 一下,然后在到达目标点的最短路 DAG 上面贪心地走就好了。注意细节。
不知道为什么我写的代码像xiang一样。
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } const int D=510,S=5505; int d,s; int dis[D][S],vis[D][S]; struct Node{ int d,s; Node(){} Node(int _d,int _s){ d=_d,s=_s; } }q[D*S]; vector <int> rev[D][S]; string ans=""; int Ha(int x,int y){ return x*100000+y; } int Q[D*S]; int main(){ d=read(),s=read(); int head=0,tail=0; memset(dis,63,sizeof dis); dis[0][0]=0,vis[0][0]=1; q[++tail]=Node(0,0); while (head<tail){ Node now=q[++head]; for (int i=(s==0);i<10;i++){ int _d=(now.d*10+i)%d; int _s=now.s+i; if (_s>s) continue; if (vis[_d][_s]){ if (dis[_d][_s]==dis[now.d][now.s]+1) rev[_d][_s].push_back(Ha(now.d,now.s)); continue; } rev[_d][_s].push_back(Ha(now.d,now.s)); vis[_d][_s]=1; dis[_d][_s]=dis[now.d][now.s]+1; q[++tail]=Node(_d,_s); } } if (!vis[0][s]) return puts("-1"),0; #define tag vis memset(tag,0,sizeof tag); head=tail=0; Q[++tail]=Ha(0,s); tag[0][s]=1; while (head<tail){ int now=Q[++head]; int nowd=dis[now/100000][now%100000]; for (auto y : rev[now/100000][now%100000]){ if (tag[y/100000][y%100000]||dis[y/100000][y%100000]!=nowd-1) continue; tag[y/100000][y%100000]=1; Q[++tail]=y; } } Node now=Node(0,0); while (now.d!=0||now.s!=s){ for (int i=(now.s==0);i<10;i++){ int _d=(now.d*10+i)%d; int _s=now.s+i; if (_s>s) continue; if (tag[_d][_s]&&dis[_d][_s]==dis[now.d][now.s]+1){ ans+=‘0‘+i; now=Node(_d,_s); break; } } } cout << ans; return 0; }
B 过的人少大概是因为题意毒瘤和读入格式毒瘤吧。直接来个 Trie ,然后 dfs 一遍就做完了。不过要注意细节。
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } const int N=200005; int n; int Next[N*34][2],c[N*34],tot=1; LL res[N]; int kr[N],cnt=0; void Add(int t,LL v,int k){ int x=1; for (int i=31;i>=k;i--){ int y=v>>i&1; if (!Next[x][y]) Next[x][y]=++tot; x=Next[x][y]; } c[x]|=1<<t; } int dfs(int x){ if (!x) return 1; int ls=Next[x][0],rs=Next[x][1]; if (!dfs(ls)||!dfs(rs)) return 0; int cc=c[ls]|c[rs]; if (c[x]==3||(c[x]==1&&(cc&2))||(c[x]==2&&(cc&1))) return 0; c[x]|=c[ls]|c[rs]; } void Out(LL v,int k,int f){ printf("%d.%d.%d.%d/%d", (int)((v>>24)&255), (int)((v>>16)&255), (int)((v>>8)&255), (int)((v>>0)&255),k); if (f) puts(""); } void solve(int x,LL v,int d){ if (!x||!c[x]) return; if (c[x]==3){ solve(Next[x][0],v,d-1); solve(Next[x][1],v|(1<<(d-1)),d-1); return; } if (c[x]==1){ cnt++; res[cnt]=v; kr[cnt]=32-d; } } int main(){ n=read(); while (n--){ char s[1000]; scanf("%s",&s); char *p=s; int t=(*p)!=‘-‘; s[strlen(s)]=‘#‘; LL v=0,k=32; for (int i=0;i<4;i++){ p++; int x=0; while (isdigit(*p)) x=(x<<1)+(x<<3)+((*p)^48),p++; v=(v<<8)|x; } if ((*p)==‘/‘){ p++; int x=0; while (isdigit(*p)) x=(x<<1)+(x<<3)+((*p)^48),p++; k=x; } Add(t,v,32-k); } if (!dfs(1)) return puts("-1"),0; solve(1,0,32); cout << cnt << endl; for (int i=1;i<=cnt;i++) Out(res[i],kr[i],i<cnt); return 0; }
C 这题是 cly 做的,我只口胡了一下。大概就是把询问的第k大转成在值域上二分。树状数组维护一下直接在树状数组上二分就好了。
这里拖陈老爷代码
#include <bits/stdc++.h> using namespace std; const int N = 1000010; typedef pair<int,int> pii; typedef long long ll; int n,k,m,mx; ll ans; vector<pii> vec[N]; #define lowbit(x) ((x) & (-(x))) ll num[N],val[N]; void add(ll* v,int p,ll val) { for ( ; p <= mx ; p += lowbit(p)) v[p] += val; } ll ask(ll* v,int p) { ll ret = 0; for ( ; p ; p -= lowbit(p)) ret += v[p]; return ret; } int query() { int t = k, ret = 0; for (int i = 20 ; i >= 0 ; -- i) { if (ret + (1 << i) <= mx) { if (t > num[ret + (1 << i)]) { t -= num[ret + (1 << i)]; ret += 1 << i; } } } return ret; } int main() { int a,b,c,d; scanf("%d%d%d",&n,&k,&m); for (int i = 1 ; i <= m ; ++ i) { scanf("%d%d%d%d",&a,&b,&c,&d); vec[a].push_back(pii(c,d)); vec[b+1].push_back(pii(-c,d)); mx = max(d,mx); } for (int i = 1 ; i <= n ; ++ i) { for (int j = 0 ; j < (int)vec[i].size() ; ++ j) { add(num,vec[i][j].second,vec[i][j].first); add(val,vec[i][j].second,1ll * vec[i][j].first * vec[i][j].second); } int tmp = query(); ans += ask(val,tmp); if (tmp < mx) ans += 1ll * (tmp + 1) * (k - ask(num,tmp)); } cout << ans << endl; return 0; }
D 略
E 设 f(x) 表示 d=x 时,能得到的答案。很容易发现这个函数很像一个凸函数。在思索一番发现这个函数从 1 开始是递增的,到达一个最高点之后,永远都不会超过这个最高点。所以直接找到这个最高点,然后小范围暴力算一算(防wa)就好了。
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } const int N=200005; int T,n,m; LL t; int p[N]; int f(int d){ int cnt=0,ans=0; LL Time=0,nowT=0; for (int i=1;i<=n&&Time<=t;i++){ if (p[i]>d) continue; if (Time+p[i]>t) break; cnt++,ans++; nowT+=p[i]; Time+=p[i]; if (cnt==m) cnt=0,Time+=nowT,nowT=0; } return ans; } int fck(int d){ int cnt=0,ans=0; LL Time=0,nowT=0; int i; for (i=1;i<=n&&Time<=t;i++){ if (p[i]>d) continue; if (Time+p[i]>t) return 0; cnt++,ans++; nowT+=p[i]; Time+=p[i]; if (cnt==m) cnt=0,Time+=nowT,nowT=0; } return i>n; } void Main(){ n=read(),m=read(),t=read(); for (int i=1;i<=n;i++) p[i]=read(); int k=0; for (int i=19;i>=0;i--) if (fck(k+(1<<i))) k|=1<<i; int ans=0,id=1; for (int i=k;i<=k+10;i++){ int v=f(i); if (v>ans) ans=v,id=i; } cout << ans << " " << min((LL)id,t)<< endl; } int main(){ T=read(); while (T--) Main(); return 0; }
F 11 的肯定全部取。然后我们取价值总和尽量大的若干对 10 和 01 ,最终 10 或者 01 会有剩余,这剩下的人的本质(是复读机)就和 00 一样了,直接和 00 一起考虑。现在我们还能取一些人,算出来可以取多少个,把 00 的人的价值按照大到小排序取前几个就好了。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=1;
char ch=getchar();
while (!isdigit(ch)&&ch!=‘-‘)
ch=getchar();
if (ch==‘-‘)
f=-1,ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x*f;
}
const int N=400005;
int n;
vector <int> v[4];
int main(){
n=read();
for (int i=1;i<=n;i++){
int a=read(),b=read();
a=(a/10)<<1|(a%10);
v[a].push_back(b);
}
for (int i=0;i<4;i++){
sort(v[i].begin(),v[i].end());
reverse(v[i].begin(),v[i].end());
}
int ans=0,m=v[3].size();
for (auto y : v[3])
ans+=y;
if (v[1].size()>v[2].size())
swap(v[1],v[2]);
int mi=m+v[1].size();
m+=v[1].size()*2;
for (int i=0;i<v[1].size();i++)
ans+=v[1][i]+v[2][i];
for (int i=v[1].size();i<v[2].size();i++)
v[0].push_back(v[2][i]);
sort(v[0].begin(),v[0].end());
reverse(v[0].begin(),v[0].end());
int re=mi*2-m;
for (int i=0;i<min(re,(int)v[0].size());i++)
ans+=v[0][i];
cout << ans;
return 0;
}
H 直接把每一个串的每一个子串信息压缩成 long long,扔到 map 里面就好了。
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } const int N=10005; int n; char s[N][10]; map <LL,int> Map; int tot[N*100],id[N*100],cnt=0; LL Ha(char *s,int n){ LL res=0; for (int i=0;i<n;i++) res=(res<<7)|s[i]; return res; } int main(){ n=read(); Map.clear(); for (int x=1;x<=n;x++){ LL c[1000]; int ct=0; scanf("%s",s[x]); int m=strlen(s[x]); for (int i=0;i<m;i++) for (int j=i;j<m;j++) c[++ct]=Ha(s[x]+i,j-i+1); sort(c+1,c+ct+1); ct=unique(c+1,c+ct+1)-c-1; for (int i=1;i<=ct;i++){ LL v=c[i]; if (!Map[v]){ Map[v]=++cnt; id[cnt]=x; } tot[Map[v]]++; } } int q=read(); while (q--){ char ss[10]; scanf("%s",ss); LL now=Ha(ss,strlen(ss)); int k=Map[now]; int ans=tot[k]; cout << ans << " "; if (ans) cout << s[id[k]] << endl; else cout << "- "; } return 0; }
I 好像做了这么多就这道非常有趣。首先我们很容易对于每一个点,算出连接它的边的颜色中,只出现一次的颜色个数。考虑每条边最多只会对于它所连接的其中一端贡献到“颜色出现两次的边”中,所以我们直接根据这些限制用网络流分配一下每一条边在哪一端是“颜色只出现一次的边”即可。(怎么又一句话说完了……)
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!=‘-‘) ch=getchar(); if (ch==‘-‘) f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar(); return x*f; } struct Edge{ int x,y,cap,nxt; Edge(){} Edge(int a,int b,int c,int d){ x=a,y=b,cap=c,nxt=d; } }; struct gragh{ static const int N=605*4,M=1000000,INF=0x7FFFFFFF; int n,S,T,fst[N],cnt; int dist[N],num[N],cur[N],p[N]; LL MaxFlow; Edge e[M]; void clear(int _n){ cnt=1,n=_n; memset(fst,0,sizeof fst); } void add(int a,int b,int c){ e[++cnt]=Edge(a,b,c,fst[a]),fst[a]=cnt; e[++cnt]=Edge(b,a,0,fst[b]),fst[b]=cnt; } void init(){ memset(dist,0,sizeof dist); memset(num,0,sizeof num); for (int i=1;i<=n;i++) num[dist[i]]++,cur[i]=fst[i]; } void init(int _S,int _T){ S=_S,T=_T; MaxFlow=0; init(); } int Augment(int &x){ int Flow=INF; for (int i=T;i!=S;i=e[p[i]].x) if (e[p[i]].cap<=Flow) Flow=e[p[i]].cap,x=e[p[i]].x; for (int i=T;i!=S;i=e[p[i]].x) e[p[i]].cap-=Flow,e[p[i]^1].cap+=Flow; return Flow; } LL ISAP(){ int x=S,y; while (dist[S]<n){ if (x==T){ MaxFlow+=Augment(x); continue; } bool found=0; for (int i=cur[x];i;i=e[i].nxt) if (dist[y=e[i].y]+1==dist[x]&&e[i].cap){ cur[x]=p[y]=i,x=y,found=1; break; } if (!found){ int d=n+1; for (int i=fst[x];i;i=e[i].nxt) if (e[i].cap) d=min(d,dist[e[i].y]+1); if (!--num[dist[x]]) return MaxFlow; num[dist[x]=d]++,cur[x]=fst[x],x=x==S?x:e[p[x]].x; } } return MaxFlow; } LL Auto(int _S,int _T){ init(_S,_T); return ISAP(); } }g; const int N=605; int T,n,m,k; int in[N],a[N],b[N]; int k1[N],k2[N],k3[N],ans[N]; vector <int> e[N]; void Out0(){ for (int i=1;i<=m;i++) printf("0 "); puts(""); } void Main(){ n=read(),m=read(),k=read(); memset(in,0,sizeof in); for (int i=1;i<=n;i++) e[i].clear(); for (int i=1;i<=m;i++){ a[i]=read(),b[i]=read(); in[a[i]]++,in[b[i]]++; } int S=n*2+m*2+1,T=S+1; g.clear(T); for (int i=1;i<=n;i++){ if (k*2<in[i]) return Out0(); int lim=min(in[i],k*2-in[i]); g.add(i,T,lim); } for (int i=1;i<=m;i++){ g.add(S,i+n,1),k1[i]=g.cnt; g.add(i+n,a[i],1),k2[i]=g.cnt; g.add(i+n,b[i],1),k3[i]=g.cnt; } g.Auto(S,T); int totcnt=0; for (int i=1;i<=m;i++) totcnt+=g.e[k1[i]].cap; if (totcnt<m) return Out0(); for (int i=1;i<=m;i++) if (g.e[k2[i]].cap){ e[b[i]].push_back(i); // printf("%d pb %d ",b[i],i); } else { e[a[i]].push_back(i); // printf("%d pb %d ",a[i],i); } int cnt=0; memset(ans,0,sizeof ans); for (int i=1;i<=n;i++) for (int j=0;j<e[i].size();j++){ if (j%2==0) cnt++; // printf(":: %d %d %d %d ",i,j,e[i][j],cnt); ans[e[i][j]]=cnt; } for (int i=1;i<=m;i++) printf("%d ",ans[i]); puts(""); } int main(){ T=read(); while (T--) Main(); return 0; }
以上是关于2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) 体验记的主要内容,如果未能解决你的问题,请参考以下文章
2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror) 体验记
2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)
2018-2019 ICPC, NEERC, Southern Subregional Contest (Online Mirror, ACM-ICPC Rules, Teams Preferred)
Codeforces 1089K - King Kog's Reception - [线段树][2018-2019 ICPC, NEERC, Northern Eurasia Finals P
2013-2014 ACM-ICPC, NEERC, Eastern Subregional Contest PART (7/10)