The 2021 ICPC Asia East Continent Final Contest (EC-Final 2021)

Preface

估计是最后一场 VP 的比赛了,这场选了四年前的 ECF 真题,然而因为中后期题里有做过的题感觉也没训到什么东西

这场后期让机给队友写几何,导致会了的 J 也没时间写,我也是直接三个半小时美美下班看云顶比赛去了


A. DFS Order

签到,下界是深度,上界是总点数减去该点的子树大小

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int t,n,dep[N],sz[N]; vector <int> v[N];
inline void DFS(CI now=1,CI fa=0)
{
    sz[now]=1; dep[now]=dep[fa]+1;
    for (auto to:v[now])
    if (to!=fa) DFS(to,now),sz[now]+=sz[to];
}
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d",&n);
        for (RI i=1;i<=n;++i) v[i].clear();
        for (RI i=1;i<n;++i)
        {
            int x,y; scanf("%d%d",&x,&y);
            v[x].push_back(y);
        }
        dep[0]=0; DFS();
        for (RI i=1;i<=n;++i)
        printf("%d %d\n",dep[i],n-sz[i]+1);
    }
    return 0;
}

B. Beautiful String

队友写的,好像是个字符串+计数的经典题,我题都没看

#include <bits/stdc++.h>

using llsi = long long signed int;
constexpr int $n = 5003;
constexpr uint32_t mod1 = 998244853, mod2 = 1000000009;

struct Hasher {
    uint32_t h1, h2;
    inline friend Hasher operator +(const Hasher &lhs, const Hasher &rhs) {
        return { (lhs.h1 + rhs.h1) % mod1, (lhs.h2 + rhs.h2) % mod2 };
    }
    inline friend Hasher operator -(const Hasher &lhs, const Hasher &rhs) {
        return { (lhs.h1 - rhs.h1 + mod1) % mod1, (lhs.h2 - rhs.h2 + mod2) % mod2 };
    }
    inline friend Hasher operator *(const Hasher &lhs, const Hasher &rhs) {
        return {
            (uint32_t)(((uint64_t)lhs.h1 * rhs.h1) % mod1),
            (uint32_t)(((uint64_t)lhs.h2 * rhs.h2) % mod2)
        };
    }
    inline friend bool operator ==(const Hasher &lhs, const Hasher &rhs) {
        return lhs.h1 == rhs.h1 && lhs.h2 == rhs.h2;
    }
} seed{42, 37}, seedExp[$n];

void prep(int n = 5000) {
    seedExp[0] = Hasher{1, 1};
    for(int i = 1; i <= n; i++) seedExp[i] = seedExp[i - 1] * seed;
}

Hasher hsh[$n];
void calc_hash(const char *s) {
    hsh[0] = {0, 0};

    for(int i = 1; s[i]; i++)
        hsh[i] = hsh[i - 1] * seed + Hasher{ (uint32_t)s[i] - '0', (uint32_t)s[i] - '0' };
}

inline Hasher substr(int l, int r) {
    return hsh[r] - hsh[l - 1] * seedExp[r - l + 1];
}

llsi ars[$n][$n];

int z[$n];
void get_z(const char *s, int n) {
    z[1] = n;
    for(int i = 2, l = 0, r = 0; i <= n; ++i) {
        if(i > r) r = i; else
        if(i + z[1 + i - l] < r) {
            z[i] = z[1 + i - l];
            continue;
        }
        l = i;
        while(r <= n && s[r] == s[1 + r - l]) r += 1;
        z[i] = r - l;
    }
    return ;
}

void work() {
    int n;
    std::string s; std::cin >> s;
    n = s.size();
    s = std::string("#") + s;
    calc_hash(s.c_str());

    for(int i = 1; i <= n; ++i) {
        memset(ars[i], 0, sizeof(int) * (n + 1));
        for(int j = 1; j <= std::min(i, n - i); ++j) {
            ars[i][j] = ars[i][j - 1] + 
                int(substr(i - j + 1, i) == substr(i + 1, i + j));
        }
        for(int j = std::min(i, n - 1) + 1; j <= n; ++j) {
            ars[i][j] += ars[i][j - 1];
        }
        for(int j = 1; j <= n; ++j) {
            ars[i][j] += ars[i][j - 1];
        }
    }
    
    // std::cerr << "ars:\n";
    // for(int i = 1; i <= n; ++i) {
    //     for(int j = 1; j <= std::min(i, n - i); ++j) {
    //         std::cerr << ars[i][j] << char(j == std::min(i, n - i) ? 10 : 32);
    //     }
    // }

    llsi ans = 0;
    for(int i = 2; i <= n; ++i) {
        get_z(s.c_str() + i - 1, n - i + 1);

        // std::cerr << "z[" << i << " .. n] = ";
        // for(int j = 1; j <= n - i + 1; ++j) std::cerr << z[j] << char(j == n - i + 1 ? 10 : 32);
        
        for(int j = 4; j <= n - i; ++j) {
            llsi hkr = std::max(std::min(z[j], j - 2), 0);
            llsi upd = ars[i - 1][hkr - 1];
            // std::cerr << upd << char(j == n - i ? 10 : 32);
            ans += upd;
        }
    }

    std::cout << ans << char(10);
    return ;
}

int main() {
    std::ios::sync_with_stdio(false);
    prep();
    int T; std::cin >> T; while(T--) work();
    return 0;
}


D. Two Walls

神秘几何,队友赛时最后一直在 Rush 这个题,但一直 WA 在二十多个点,只能说几何是这样的


E. Prof. Pang and Poker

思博题,想清楚就不难的一个题

\(P\) 的那张牌权值为 \(p\),首先按顺序判掉一些显然的 Case:

  • \(n=1\),此时必败
  • \(A,B\) 中任意一个人没有 \(<p\) 的牌,此时必败
  • \(B\)\(<p\) 的牌数量 \(>1\),此时必胜,因为此时 \(A\) 可以把出牌权丢给 \(B\) 然后让其一直出,最后总能获胜

\(B\) 那张 \(<p\) 的牌权值为 \(q\),对于剩下的局面,想要获胜需要满足以下四个条件:

  • \(A\)\(<p\) 的牌数量 \(>1\)
  • \(A\) 所有牌的最大值大于 \(B\) 所有牌的最大值
  • \(A\) 存在一张权值在 \([q,p)\) 中的牌
  • \(n\ge 4\)

策略的话就是让 \(A\) 先出一张无关紧要的 \(<p\) 的牌,然后把出牌权过给 \(B\)

\(B\) 一直出牌,直到 \(B\) 手中只剩下 \(q\) 时将其管上,然后打出权值在 \([q,p)\) 中的牌的即可

其中 \(n\ge 4\) 的条件非常隐蔽,因为 \(n=3\) 的极端情况 \(A\) 出完权值在 \([q,p)\) 中的牌后会直接跑掉

最后还需要注意特判 \(m=1\) 的情况,具体实现看代码

#include<cstdio>
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
int t,n,m;
int main()
{
    ios::sync_with_stdio(false); cin.tie(0);
    for (cin>>t;t;--t)
    {
        cin>>n>>m;
        vector <int> A,B;
        auto trs=[&](const char& ch)
        {
            if ('2'<=ch&&ch<='9') return ch-'0'; else
            if (ch=='T') return 10; else
            if (ch=='J') return 11; else
            if (ch=='Q') return 12; else
            if (ch=='K') return 13; else 
            return 14;
        };
        for (RI i=1;i<=n;++i)
        {
            string s; cin>>s;
            A.push_back(trs(s[0]));
        }
        for (RI i=1;i<=m;++i)
        {
            string s; cin>>s;
            B.push_back(trs(s[0]));
        }
        string s; cin>>s;
        int P=trs(s[0]);
        if (n==1) { puts("Shou"); continue; }
        int less_A=0,less_B=0;
        for (auto x:A) if (x<P) ++less_A;
        for (auto x:B) if (x<P) ++less_B;
        if (less_A==0||less_B==0) { puts("Shou"); continue; }
        if (m==1)
        {
            int Q;
            for (auto x:B) if (x<P) Q=x;
            bool grt_Q_less_P=0;
            for (auto x:A) if (x>=Q&&x<P) grt_Q_less_P=1;
            puts(grt_Q_less_P?"Pang":"Shou");
            continue;
        }
        if (less_B>1) { puts("Pang"); continue; }
        int Q;
        for (auto x:B) if (x<P) Q=x;
        bool grt_Q_less_P=0;
        for (auto x:A) if (x>=Q&&x<P) grt_Q_less_P=1;
        int max_A=*max_element(A.begin(),A.end());
        int max_B=*max_element(B.begin(),B.end());
        if (n>=4&&grt_Q_less_P&&less_A>=2&&max_A>max_B) puts("Pang"); else puts("Shou");
    }
    return 0;
}

H. Check Pattern is Good

暑假前集训做过这题,做法可以看 这里

但是这题还需要输出方案,二分图的最大独立集方案的构造可以参考 这里

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<array>
#include<assert.h>
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
int t,n,m,tp[N][N],num[N][N][2]; char s[N][N]; array <int,3> rst[N*N<<1];
namespace Network_Flow
{
	const int NN=20005,M=10*NN,INF=1e9;
	struct edge
	{
		int to,nxt,v;
	}e[M<<1]; int cnt,head[NN],cur[NN],dep[NN],S,T,in_match[NN],tag[NN]; queue <int> q;
	inline void init(void)
	{
		cnt=1; memset(head,0,T+1<<2); memset(in_match,0,T+1<<2); memset(tag,0,T+1<<2);
	}
    inline void addedge(CI x,CI y,CI z)
    {
        e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
        e[++cnt]=(edge){x,head[y],0}; head[y]=cnt;
    }
    #define to e[i].to
    inline bool BFS(void)
    {
        memset(dep,0,T+1<<2); dep[S]=1; q=queue <int>(); q.push(S);
        while (!q.empty())
        {
            int now=q.front(); q.pop();
			for (RI i=head[now];i;i=e[i].nxt)
            if (e[i].v&&!dep[to]) dep[to]=dep[now]+1,q.push(to);
        }
        return dep[T];
    }
    inline int DFS(CI now,CI tar,int dis)
    {
        if (now==tar) return dis; int ret=0;
        for (RI& i=cur[now];i&&dis;i=e[i].nxt)
        if (e[i].v&&dep[to]==dep[now]+1)
        {
            int dist=DFS(to,tar,min(dis,e[i].v));
            dis-=dist; ret+=dist; e[i].v-=dist; e[i^1].v+=dist;
        }
        if (!ret) dep[now]=0; return ret;
    }
    inline void travel(CI now)
    {
        if (tag[now]) return; tag[now]=1;
		for (RI i=head[now];i;i=e[i].nxt)
        if (to!=S&&to!=T&&e[i].v) travel(to);
    }
    #undef to
    inline int Dinic(int ret=0)
    {
        while (BFS()) memcpy(cur,head,T+1<<2),ret+=DFS(S,T,INF); return ret;
    }
};
using namespace Network_Flow;
int main()
{
	for (scanf("%d",&t);t;--t)
	{
		RI i,j,p,q; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%s",s[i]+1);
		if (n==1||m==1)
        {
            puts("0");
            for (RI i=1;i<=n;++i,putchar('\n'))
            for (RI j=1;j<=m;++j)
            {
                if (s[i][j]=='?') s[i][j]='B';
                putchar(s[i][j]);
            }
            continue;
        }
		int idx=0; S=0; T=2*n*m+1; init();
		for (i=1;i<=n;++i) for (j=1;j<=m;++j)
		if ((i+j)&1) if (s[i][j]!='?') s[i][j]=(s[i][j]=='B'?'W':'B');
        vector <int> L,R;
		for (i=1;i<n;++i) for (j=1;j<m;++j)
		{
			int cnt_W=0,cnt_B=0;
			for (p=0;p<2;++p) for (q=0;q<2;++q)
			{
				if (s[i+p][j+q]=='W') ++cnt_W;
				if (s[i+p][j+q]=='B') ++cnt_B;
			}
			if (!cnt_W&&!cnt_B)
			{
				rst[num[i][j][0]=++idx]={i,j,0};
                rst[num[i][j][1]=++idx]={i,j,1};
                L.push_back(num[i][j][0]); R.push_back(num[i][j][1]);
				tp[i][j]=2; addedge(num[i][j][0],num[i][j][1],1);
				addedge(S,num[i][j][0],1); addedge(num[i][j][1],T,1);
			} else if (!cnt_B)
			{
				rst[num[i][j][0]=++idx]={i,j,0};
                L.push_back(num[i][j][0]);
                tp[i][j]=0;	addedge(S,num[i][j][0],1);
			} else if (!cnt_W)
			{
				rst[num[i][j][1]=++idx]={i,j,1};
                R.push_back(num[i][j][1]);
                tp[i][j]=1; addedge(num[i][j][1],T,1);
			} else tp[i][j]=3;
		}
		auto check=[&](CI x,CI y,CI c)
		{
			if (tp[x][y]==3) return false; if (tp[x][y]==2) return true; return tp[x][y]==c;
		};
		for (i=1;i<=n;++i) for (j=1;j<=m;++j) if (s[i][j]=='?')
		{
			if (i-1<1||i+1>n||j-1<1||j+1>m) continue;
			if (check(i,j-1,0)&&check(i-1,j,1)) addedge(num[i][j-1][0],num[i-1][j][1],1);
			if (check(i-1,j,0)&&check(i,j-1,1)) addedge(num[i-1][j][0],num[i][j-1][1],1);
			if (check(i-1,j-1,0)&&check(i,j,1)) addedge(num[i-1][j-1][0],num[i][j][1],1);
			if (check(i,j,0)&&check(i-1,j-1,1)) addedge(num[i][j][0],num[i-1][j-1][1],1);
		}
		for (i=1;i<=n;++i) for (j=1;j<m;++j) if (s[i][j]=='?'&&s[i][j+1]=='?')
		{
			if (i-1<1||i+1>n) continue;
			if (check(i-1,j,0)&&check(i,j,1)) addedge(num[i-1][j][0],num[i][j][1],1);
			if (check(i,j,0)&&check(i-1,j,1)) addedge(num[i][j][0],num[i-1][j][1],1);
		}
		for (i=1;i<n;++i) for (j=1;j<=m;++j) if (s[i][j]=='?'&&s[i+1][j]=='?')
		{
			if (j-1<1||j+1>m) continue;
			if (check(i,j-1,0)&&check(i,j,1)) addedge(num[i][j-1][0],num[i][j][1],1);
			if (check(i,j,0)&&check(i,j-1,1)) addedge(num[i][j][0],num[i][j-1][1],1);
		}
        int res=idx-Dinic();
		printf("%d\n",res);
        for (auto x:L)
		for (RI i=head[x];i;i=e[i].nxt)
        if (e[i].to!=S&&e[i].v==0) in_match[x]=in_match[e[i].to]=1;
        for (auto x:L)
        {
            if (in_match[x]) continue;
            travel(x);
        }
        vector <int> ans;
        for (auto x:L)
        if (tag[x]) ans.push_back(x);
        for (auto x:R)
        if (!tag[x]) ans.push_back(x);
        assert((int)ans.size()==res);
        for (auto id:ans)
        {
            auto [x,y,c]=rst[id];
			for (p=0;p<2;++p) for (q=0;q<2;++q)
            if (s[x+p][y+q]=='?') s[x+p][y+q]=(c?'B':'W');
        }
        for (RI i=1;i<=n;++i) for (RI j=1;j<=m;++j)
        if (s[i][j]=='?') s[i][j]='B';
        for (i=1;i<=n;++i) for (j=1;j<=m;++j)
		if ((i+j)&1) s[i][j]=s[i][j]=='B'?'W':'B';
        for (RI i=1;i<=n;++i,putchar('\n'))
        for (RI j=1;j<=m;++j) putchar(s[i][j]);
	}
	return 0;
}

I. Future Coder

队友写的签,我题意都不知道

#include <bits/stdc++.h>

void work() {
    int n; std::cin >> n;
    int64_t ans = 0;
    int n2 = 0, n1 = 0, ze = 0, p1 = 0, p2 = 0;
    while(n--) {
        int a; std::cin >> a;
        if(a <= -2) {
            ans += p1 + p2;
            n2 += 1;
        } else
        if(a == -1) {
            ans += p1 + p2;
            n1 += 1;
        } else
        if(a == 0) {
            ans += p1 + p2;
            ze += 1;
        } else
        if(a == 1) {
            ans += n2 + n1 + ze + p1 + p2;
            p1 += 1;
        } else
        /* a >= 2 */ {
            ans += n2 + n1 + ze      + p1;
            p2 += 1;
        }
    }
    std::cout << ans << char(10);
}

int main() {
    std::ios::sync_with_stdio(false);
    int T; std::cin >> T; while(T--) work();
    return 0;
}

J. Elden Ring

本质最短路题,赛后补了下发现非常好写

显然 \(A\le B\)\(A>B\) 是两个问题,因为此时每个点能访问的时间是一段前缀/后缀

前者非常简单,因为沿着最短路走一定最优,只不过中途有些点可能会无法访问,直接 BFS 一遍即可

后者考虑求出每个点最早访问时间 \(t_i\) 后,其实就是把最短路的转移结果对 \(t_i\)\(\max\)

但需要注意的是我们要判断在某个时刻能否真的走到一个点

考虑 dijkstra 的过程中,我们是按 \(dis\) 顺序枚举点的,因此可以很容易地维护出 \(dis\) 小于某个点的点数量,用这个值判断能否激活该点即可

具体实现看代码

#include<cstdio>
#include<iostream>
#include<vector>
#include<queue>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005,INF=1e9;
int t,n,m,A,B,l[N],dis[N]; vector <int> v[N];
namespace Case1 // A<=B
{
    inline void solve(void)
    {
        queue <int> q;
        dis[1]=0; q.push(1);
        while (!q.empty())
        {
            int now=q.front(); q.pop();
            for (auto to:v[now])
            {
                if (dis[to]!=INF) continue;
                if (l[1]+1LL*(A-B)*dis[now]<=l[to]) continue;
                dis[to]=dis[now]+1; q.push(to);
            }
        }
    }
}
namespace Case2 // A>B
{
    inline void solve(void)
    {
        static int vis[N];
        for (RI i=1;i<=n;++i) vis[i]=0;
        for (RI i=2;i<=n;++i)
        if (l[i]<l[1]) l[i]=1; else l[i]=(l[i]-l[1])/(A-B)+2;
        priority_queue <pair <int,int>> hp;
        l[1]=dis[1]=0; hp.push({0,1}); int cnt=0;
        while (!hp.empty())
        {
            auto [_,now]=hp.top(); hp.pop();
            if (vis[now]) continue; vis[now]=1;
            if (cnt<dis[now]) { dis[now]=INF; continue; }
            ++cnt;
            for (auto to:v[now])
            {
                if (dis[to]>max(dis[now]+1,l[to]))
                {
                    dis[to]=max(dis[now]+1,l[to]);
                    hp.push({-dis[to],to});
                }
            }
        }
    }
}
int main()
{
    for (scanf("%d",&t);t;--t)
    {
        scanf("%d%d%d%d",&n,&m,&A,&B);
        for (RI i=1;i<=n;++i) dis[i]=INF,v[i].clear();
        for (RI i=1;i<=m;++i)
        {
            int x,y; scanf("%d%d",&x,&y);
            v[x].push_back(y); v[y].push_back(x);
        }
        for (RI i=1;i<=n;++i)
        {
            scanf("%d",&l[i]);
            if (i>=2) l[i]+=B;
        }
        if (A<=B) Case1::solve(); else Case2::solve();
        if (dis[n]==INF) puts("-1"); else printf("%d\n",dis[n]);
    }
    return 0;
}

L. Fenwick Tree

队友写的,我题目都没看

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5+5;
set<int> cnt[N];
int lb(int x) {return x&(-x);}

int solve() {
    int n; cin >> n;
    int ans = 0;
    string str; cin >> str; str = '0'+str;
    for (int i=1; i<=n; ++i) cnt[i].clear();
    for (int i=1; i<=n; ++i) {
        if (str[i]=='1') {
            if (cnt[i].empty()) {
                ++ans;
                for (int j=i; j<=n; j+=lb(j)) cnt[j].insert(i);
            } else {
                for (int j=i+lb(i); j<=n; j+=lb(j)) {
                    for (int x : cnt[i]) cnt[j].erase(x);
                }
                cnt[i].clear();
                for (int j=i; j<=n; j+=lb(j)) cnt[j].insert(i);

            }
        } else {
            if (cnt[i].size() == 1) ++ans;
            for (int j=i+lb(i); j<=n; j+=lb(j)) {
                for (int x : cnt[i]) cnt[j].erase(x);
            }
            cnt[i].clear();
        } 
    }
    return ans;
}

signed main() {
    ios::sync_with_stdio(0); cin.tie(0);
    int T; cin >> T; while (T--) cout << solve() << '\n';
    return  0;
}

Postscript

感觉现在的自己非常缺少对于算竞的 passion 啊,再也没法在 VP 或是比赛中收获快乐了,感觉是真的到了要告别的时候了

posted @ 2026-01-30 14:21  空気力学の詩  阅读(0)  评论(0)    收藏  举报