CodeChef April Challenge 2019 Division 2

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ginenn/article/details/89328811


前言

比赛地址:https://www.codechef.com/APRIL19B
CodeChef是世界著名的算法竟赛网站,而它每个月的举办的long challenge contest以有偿付费出题的高质量而闻名。比赛为期十天,期间要独立解决8个左右的由易到难的算法编程题目,严禁作弊行为。比赛分为div1和div2,前者的难度很大,参加的要求是rating>=1800,通常参加人数不超过千人,后者的难度较之相对容易,参加人数通过超过一万,但是两者会有一些问题是公共的。比赛结束后,所有代码公开,任何人都可以查看和学习,同时讨论区也会发布详细的英文题解。
这篇题解仅包括div2的七道题目,除去最后一道特殊的交互题。

因为我知识水平有限,如有错误或者纰漏,还望指出,如有疑问,也欢迎交流。
QQ:1434287907


Maximum Remaining

题意

给你一个大小为 N N 的序列 A A ,求 m a x ( A i max(A_i % A j ) A_j)

数据范围

2 < = N = 1000 , 1 < = A i < = 1 0 9 2<=N=1000,1<=A_i<=10^9

题解

显然是取最大的两个不同的数, A [ i ] < A [ j ] A[i]<A[j] ,答案就是 A [ i ] A[i] % A [ j ] A[j]
值得注意的是,当所有数相同时,答案为 0 0

代码

int main()
{
    int n;
    cin>>n;
    vector<int> a(n);
    for(int i=0; i<n; i++) cin>>a[i];
    sort(a.rbegin(),a.rend());
    for(int i=1; i<n; i++)
        if(a[i]!=a[i-1]) return cout<<a[i],0;
    cout<<0;
    return 0;
}

Friend or Girlfriend

题意

给你一个长度为 N N 的字符串 S S 和一个字符 X X ,求有多少个包含 X X 的不同子串

数据范围

1 &lt; = T &lt; = 1000 N &lt; = 1 0 6 1&lt;=T&lt;=1000,\sum N&lt;=10^6

题解

从后往前遍历字符串,维护在字符串中最早出现的 X X 的下标 t t ,那么对于字符串中的每一个字符, S [ i ] S[i] S [ n 1 ] S[n-1] 这段字符串中,包含 X X 的子串数量即为 n t n-t

代码

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        cin>>s>>c;
        ll ans=0;
        for(int i=n-1,t=-1; ~i; i--)
        {
            if(s[i]==c) t=i;
            if(~t) ans+=n-t;
        }
        cout<<ans<<endl;
    }
    return 0;
}

Fencing

题意

给以一个 N M N*M 大小的菜地,有 K K 个菜地格子里种了菜,它们的坐标为 ( r i c i ) (r_i,c_i) ,剩下的格子全部是杂草,让你用围栏把所有中了菜的格子围起来,求最小的围栏长度。

数据范围

1 T 10 , 1 N , M 1 0 9 , 1 K 1 0 5 , 1 r N , 1 c M 1≤T≤10,1≤N,M≤10^9,1≤K≤10^5,1≤r≤N,1≤c≤M

题解

基于 m a p map D F S DFS

代码

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

#define fi first
#define se second

typedef long long ll;
typedef pair<int,int> pi;
typedef vector<pi> vpi;

const int N=1000007;

int n,m,k,ans;
map<pi,bool> f,vis;

void dfs(pi t)
{
    vis[t]=1;
    if(!f[{t.fi+1,t.se}]) ans++;
    else if(!vis[{t.fi+1,t.se}]) dfs({t.fi+1,t.se});
    if(!f[{t.fi-1,t.se}]) ans++;
    else if(!vis[{t.fi-1,t.se}]) dfs({t.fi-1,t.se});
    if(!f[{t.fi,t.se+1}]) ans++;
    else if(!vis[{t.fi,t.se+1}]) dfs({t.fi,t.se+1});
    if(!f[{t.fi,t.se-1}]) ans++;
    else if(!vis[{t.fi,t.se-1}]) dfs({t.fi,t.se-1});
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>m>>k;
        f.clear(),vis.clear(),ans=0;
        vpi p(k);
        for(int i=0; i<k; i++) cin>>p[i].fi>>p[i].se,f[p[i]]=1;
        for(int i=0; i<k; i++) if(!vis[p[i]]) dfs(p[i]);
        cout<<ans<<endl;
    }
    return 0;
}


Subtree Removal

题意

给定 N N 个节点的有根树(节点编号为 1 N 1 ∼ N ),根节点编号为 1 1 ,每个节点都有点权 A i Ai
你可以任意次(包括零次)进行下面的操作:选择树中的某个节点,并删去包括该节点在内的整棵子树。
记收益为树中剩下的节点的点权之和减去 X × k X × k ,其中 k k 代表操作次数。请求出最大收益。

数据范围

1 T 10 , 1 N 1 0 5 , 1 X 1 0 9 , 1 u , v N , A i 1 0 9 1≤T≤10,1≤N≤10^5,1≤X≤10^9,1≤u,v≤N,|Ai|≤10^9

题解

树DP
对于每一个节点,若它所有的子节点的子树的最大收益之和 s u m sum 大于 X -X ,则这个节点的子树的最大收益为 s u m sum ,否则就要删除这颗子树,即这个节点的子树的最大收益为 X -X

代码

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

#define eb emplace_back

typedef long long ll;
typedef vector<int> vi;

const int N=100007;

int n;
ll x;
ll a[N];
vi g[N];

ll solve(int u, int fa)
{
    ll sum=a[u];
    for(auto v: g[u])
    {
        if(v==fa) continue;
        sum+=solve(v,u);
    }
    return max(sum,-x);
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n>>x;
        for(int i=1; i<=n; i++) cin>>a[i],g[i].clear();
        for(int i=1; i<n; i++)
        {
            int u,v;
            cin>>u>>v;
            g[u].eb(v);
            g[v].eb(u);
        }
        cout<<solve(1,0)<<endl;
    }
    return 0;
}

Playing with Numbers

题意

给定 N N 个节点的有根树(节点编号为 1 N 1 ∼ N ),根节点编号为 1 1 。每个节点 i i 拥有点权 v i v_i 和另一个参数 m i m_i
没有儿子的节点被称作叶子节点。记树中叶子节点的个数为 L L ,编号按升序排列为 l 1 , l 2 , . . . , l L l_1, l_2, . . . , l_L
我们按照如下方法定义叶子节点 l i l_i 的答案:
• 对于从根节点到 l i l_i 的路径上的每个节点,选择一个非负整数,乘以节点的点权。
• 对路径上所有节点按照上述方式算出来的值求和。
• 叶子 l i l_i 的答案 a i a_i 就是和对 m l j m_{l_j} 取模的最大值。
请求出每个叶子节点的答案。

数据范围

1 T 8 , 2 N 1 0 5 , 1 x , y N , 1 v i 1 0 18 , 1 m i 1 0 18 1≤T≤8,2≤N≤10^5,1≤x,y≤N,1≤vi≤10^{18},1≤mi≤10^{18}

题解

拓展贝祖(裴蜀)定理
贝祖定理:二元一次不定方程 a x + b y = c ax+by=c 存在整数解的充分必要条件是 g c d ( a , b ) c gcd(a,b)|c
扩展贝祖定理:改成 n n 元一次不定方程,结论依然成立。
d p [ i ] dp[i] 表示从根节点到节点 i i g c d gcd ,那么对于每一个叶子节点来说,它的答案就是
m i g c d ( d p [ i ] , m i ) m_i-gcd(dp[i],m_i)
至于为什么,可以感性的理解一下

代码

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

#define eb emplace_back

typedef long long ll;
typedef vector<int> vi;

const int N=100007;

int n;
ll v[N],m[N],dp[N];
bool vis[N];
vi g[N];

void solve(int u, int fa, ll t)
{
    dp[u]=t;
    bool ok=1;
    for(auto x: g[u])
    {
        if(x!=fa)
        {
            solve(x,u,__gcd(dp[u],v[x]));
            ok=0;
        }
    }
    if(ok) vis[u]=1;
}

int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>n;
        memset(vis,0,sizeof vis);
        for(int i=1; i<=n; i++) g[i].clear();
        for(int i=1; i<n; i++)
        {
            int u,v;
            cin>>u>>v;
            g[u].eb(v);
            g[v].eb(u);
        }
        for(int i=1; i<=n; i++) cin>>v[i];
        for(int i=1; i<=n; i++) cin>>m[i];
        solve(1,0,v[1]);
        for(int i=2; i<=n; i++)
            if(vis[i])
                cout<<m[i]-__gcd(m[i],dp[i])<<' ';
        cout<<endl;
    }
    return 0;
}


Kira Loves Palindromes

题意

给定字符串 S S ,请求出有多少种方案可以从该字符串中选出两个不相交的非空子串 s 1 s1 s 2 s2 ,使得其串接 s 1 + s 2 s1+s2 为回文串。

数据范围

1 S 1000 1 ≤ |S| ≤ 1000

题解

d p [ i ] [ j ] dp[i][j] 表示 s [ i ] = = s [ j ] s[i]==s[j] 时选的第一个字串开头为 s [ i ] s[i] ,选的第二个字串以 s [ j ] s[j] 为结尾, s [ i ] s[i] s [ j ] s[j] 这段字符串中符合题目要求的答案
转移方程为: d p [ i ] [ j ] = d p [ i + 1 ] [ j 1 ] + f [ i + 1 ] [ j 1 ] + b [ i + 1 ] [ j 1 ] + 1 dp[i][j]=dp[i+1][j-1]+f[i+1][j-1]+b[i+1][j-1]+1
f [ i ] [ j ] f[i][j] 表示 s [ i ] s[i] s [ j ] s[j] 的字符串中以 s [ i ] s[i] 开头的回文串个数
b [ i ] [ j ] b[i][j] 表示 s [ i ] s[i] s [ j ] s[j] 的字符串中以 s [ j ] s[j] 结尾的回文串个数
所有 d p [ i ] [ j ] dp[i][j] 之和就是答案

代码

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

#define eb emplace_back

typedef long long ll;

const int N=1007;

int n;
ll ans;
bool ok[N][N];
int f[N][N],b[N][N];
ll dp[N][N];
char s[N];

void pre()
{
    for(int i=n-1; i>=0; i--)
			for(int j=i; j<n; j++)
				ok[i][j]=((s[i]==s[j])&&(j-i<3||ok[i+1][j-1]));
    for(int i=0; i<n; i++) f[i][i]=b[i][i]=1;
    for(int i=0; i<n; i++)
        for(int j=i+1; j<n; j++)
        {
            if(ok[i][j]) f[i][j]=f[i][j-1]+1;
            else f[i][j]=f[i][j-1];
        }
    for(int j=n-1; j>=0; j--)
        for(int i=j-1; i>=0; i--)
        {
            if(ok[i][j]) b[i][j]=b[i+1][j]+1;
            else b[i][j]=b[i+1][j];
        }
}

void solve()
{
    if(n>1) for(int i=0; i+1<n; i++) if(s[i]==s[i+1]) dp[i][i+1]=1,ans++;
    for(int l=3; l<=n; l++)
        for(int i=0; i+l-1<n; i++)
        {
            int j=i+l-1;
            if(s[i]==s[j])
            {
                dp[i][j]=f[i+1][j-1]+b[i+1][j-1]+dp[i+1][j-1]+1;
                ans+=dp[i][j];
            }
        }
}

int main()
{
    cin>>s;
    n=strlen(s);
    pre();
    solve();
    cout<<ans;
    return 0;
}

Mininum XOR over Tree

题意

给定 N N 个节点的有根树(节点编号为 1 N 1 ∼ N ),根节点编号为 1 1 。每个节点都有点权 w i w_i
你需要回答 Q 个询问。询问必须在线回答,即:只有回答了上一个询问,才能知道下一个询问。
每个询问中给定节点 v v 和参数 k k 。对于 v v 的子树(包括 v v )中的所有节点 u u ,求 w u k w_u⊕ k 的最大值(⊕ 代表按位异或操作)。此外,求出能达到这一最大值的最小节点编号 u u

数据范围

1 T 1000 , 1 N 2 1 0 5 , 1 Q 1 0 6 , 1 x , y , v N 1≤T≤1000, 1≤N≤2*10^5, 1≤Q≤10^6, 1≤x,y,v≤N
1 w i &lt; 2 20 , 1 k &lt; 220 1≤w_i&lt;2^{20}, 1≤k&lt;220

题解

可持久化01字典树合并
t a g [ i ] tag[i] 表示字典树节点编号为 i i 表示的点权的最小编号

代码

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

#define fi first
#define se second
#define lc (rt<<1)
#define rc (rt<<1|1)
#define mp make_pair
#define ep emplace_pop
#define eb emplace_back
#define sz(x) (int)(x).size()
#define all(x) x.begin(),x.end()
#define rall(x) x.rbegin(),x.rend()
#define mst(a,v) memset(a,v,sizeof(a))
#define debug(x) cout<<#x": "<<x<<'\n';
#define IOS {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}

typedef long long ll;
typedef long double ld;
typedef pair<int,int> pi;
typedef pair<ll,ll> pl;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef vector<ld> vd;
typedef vector<pi> vpi;
typedef vector<pl> vpl;

const int K=20;
const int N=200007;
const int M=1111111;
const int mod=1e9+7;
const ld PI=acos(-1.0);
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3fLL;

template<class T> bool re(T &x)
{
    int f=1;
    char c=getchar();
    for( ; !isdigit(c); c=getchar())
    {
        if(c==-1) return false;
        if(c==45) f=-1;
    }
    for(x=c^48; isdigit(c=getchar()); x=(x<<3)+(x<<1)+(c^48));
    return x*=f,true;
}

template<class T> inline void gmax(T &A,T B)
{
    (A<B)&&(A=B);
}

template<class T> inline void gmin(T &A,T B)
{
    (A>B)&&(A=B);
}

int n,m,u,x,tot,cnt;
int h[N],ne[N<<1],p[N<<1],w[N],tag[2*N*22],root[N],trie[2*N*22][2];

void add(int u, int v)
{
    p[++tot]=v,ne[tot]=h[u],h[u]=tot;
}

int insert(int x, int k, int i)
{
    int u=++cnt;
    if(k<0)
    {
        tag[u]=i;
        return u;
    }
    int t=(x>>k)&1;
    trie[u][t]=insert(x,k-1,i);
    trie[u][t^1]=0;
    return u;
}

int merge(int x, int y, int k)
{
    if(!x) return y;
    if(!y) return x;
    if(k<0) return (tag[x]<tag[y]? x:y);
    int t=++cnt;
    trie[t][1]=merge(trie[x][1],trie[y][1],k-1);
    trie[t][0]=merge(trie[x][0],trie[y][0],k-1);
    return t;
}

int query(int u, int x, int k)
{
    if(k<0) return tag[u];
    int t=(x>>k)&1;
    if(trie[u][t^1]) return query(trie[u][t^1],x,k-1);
    else return query(trie[u][t],x,k-1);
}

void dfs(int u, int fa)
{
    for(int i=h[u]; i; i=ne[i]) if(p[i]!=fa) dfs(p[i],u);
    for(int i=h[u]; i; i=ne[i]) if(p[i]!=fa) root[u]=merge(root[u],root[p[i]],K);
}

int main()
{
    int T;
    re(T);
    while(T--)
    {
        re(n),re(m);
        u=x=tot=cnt=0;
        for(int i=1; i<=n; i++) h[i]=0;
        for(int i=1; i<=n; i++)
        {
            re(w[i]);
            root[i]=insert(w[i],K,i);
        }
        for(int i=1; i<n; i++)
        {
            int u,v;
            re(u),re(v);
            add(u,v),add(v,u);
        }
        dfs(1,0);
        for(int i=0; i<m; i++)
        {
            int a,b;
            re(a),re(b);
            u^=a,x^=b;
            u=query(root[u],x,K);
            x=w[u]^x;
            printf("%d %d\n",u,x);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ginenn/article/details/89328811