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;
}
CF1070A

 

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;
}
CF1070B

 

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;
}
CF1070C

 

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;
}
CF1070E

 

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;
}
CF1070F

 

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;
}
CF1070H

 

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;
}
CF1070I

 

以上是关于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)

[算法竞赛入门经典]Kickdown ACM/ICPC NEERC 2004,UVa1587