2019 The Preliminary Contest for ICPC China Nanchang National Invitational

传送门:

Problem A

温暖的签到题,打个表就行了。

吐槽一下:出题人并没有说用什么格式输出,害得我输出一行怒得一pe

#include <bits/stdc++.h>
using namespace std;
int main()
{
    cout<<6<<endl;
    cout<<28<<endl;
    cout<<496<<endl;
    cout<<8128<<endl;
    cout<<33550336<<endl;
    return 0;
}

Problem B


Problem C


Problem D


Problem E


Problem F


Problem G


Problem H

规律题,经过推倒发现答案为:3n243^{n-2}*4

快速幂搞搞就万事了。

#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll powm(ll x,ll n){
    ll res=1;
    while (n){
        if (n&1) res=res*x%mod;
        n>>=1;
        x=x*x%mod;
    }
    return res;
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    ll n,ans=0;
    cin>>n;
    if (n==1) ans=1;
    else ans=powm(3,n-2)*4%mod;
    cout<<ans<<endl;
    return 0;
}

Problem I

题意:

给你一个长度为nn的序列,让你找到一个区间[l,r][l,r],使得i=lrai×mini=lrai\sum_{i=l}^{r}{a_i}\times\min_{i=l}^{r}{a_i}最大

其中,106ai106-10^6\le a_i\le10^6

分析:

如果aia_i不能取负数,则是poj2796\text{poj2796}的原题,我们用单调栈对每一个aia_i处理出以aia_i为最小值,最左/最右能扩展到的位置,之后直接贪心扫即可。

而在这个题中,因为涉及负数×负数的情况,则我们不能直接累加某段区间的和。

我们考虑用线段树去记录区间前缀和的最大值以及最小值。我们可以贪心的去思考,倘若当前的aia_i大于00,则显然是取区间[i,ar][i,a_r]中的最大值maxmax,以及区间[al,i][a_l,i]中的最小值minminmaxminmax-min即为以aia_i为最小值的最大区间和。同理,对于ai&lt;0a_i&lt;0的情况,我们只需尽量取区间[al,ar][a_l,a_r]中的最小区间和即可。

#include <bits/stdc++.h>
using namespace std;
const int maxn=500005;
int a[maxn];
long long sum[maxn];
int n,m;
struct node
{
    int l,r;
    long long mx,add,mn;
}tr[maxn*4];
void push_up(int k){
    tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
    tr[k].mn=min(tr[k<<1].mn,tr[k<<1|1].mn);
}
void build(int k,int l,int r)
{
    tr[k].l=l,tr[k].r=r;
    tr[k].add=0;
    if(l==r){
        tr[k].mx=sum[l];
        tr[k].mn=sum[l];
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    push_up(k);
}
void pushdown(int k)
{
    if(tr[k].add!=0)
    {
        tr[k<<1].add+=tr[k].add;
        tr[k<<1].mx+=tr[k].add;
        tr[k<<1].mn+=tr[k].add;
        tr[k<<1|1].add+=tr[k].add;
        tr[k<<1|1].mx+=tr[k].add;
        tr[k<<1|1].mn+=tr[k].add;
        tr[k].add=0;
    }
}
void update(int k,int s,int t,int v)
{
    int l=tr[k].l,r=tr[k].r;
    if(l>t||r<s) return;
    if(l>=s&&r<=t)
    {
        tr[k].mx+=v;
        tr[k].mn+=v;
        tr[k].add+=v;
        return;
    }
    pushdown(k);
    update(k<<1,s,t,v);
    update(k<<1|1,s,t,v);
    push_up(k);
}
long long query(int k,int s,int t)
{
    int l=tr[k].l,r=tr[k].r;
    if(l>t||r<s) return 0;
    if(l>=s&&r<=t) return tr[k].mx;
    pushdown(k);
    return max(query(k<<1,s,t),query(k<<1|1,s,t));
}
long long query1(int k,int s,int t)
{
    int l=tr[k].l,r=tr[k].r;
    if(l>t||r<s) return 0;
    if(l>=s&&r<=t) return tr[k].mn;
    pushdown(k);
    return min(query1(k<<1,s,t),query1(k<<1|1,s,t));
}
void read(int &ret){
    ret=0;
    char ch=getchar();
    int flag=1;
    while(ch>'9'||ch<'0'){if(ch=='-')flag=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
        ret=ret*10+ch-'0';
        ch=getchar();
    }
    ret*=flag;
}
int L[maxn],R[maxn];
long long ans1[maxn],ans2[maxn];
int main()
{
    //freopen("in.txt","r",stdin);
    read(n);
    //cin>>n;
    for(int i=1;i<=n;i++){
        read(a[i]);
        //cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        L[i]=R[i]=i;
    }
    a[0]=a[n+1]=-0x3f3f3f3f;
    for(int i=1;i<=n;i++){
        while(a[i]<=a[L[i]-1])
            L[i]=L[L[i]-1];
    }
    for(int i=n;i>=1;i--){
        while(a[i]<=a[R[i]+1])
            R[i]=R[R[i]+1];
    }
    int r=1;
    a[0]=a[n+1]=0;
    build(1,1,n);
    for(int i=1;i<=n;i++){
        update(1,i,n,-a[i-1]);
        if(a[i]>=0)ans1[i]=query(1,i,R[i]);
        else ans1[i]=query1(1,i,R[i]);
    }
    for(int i=n;i;i--){sum[i]=sum[i+1]+a[i];}
    build(1,1,n);
    for(int i=n;i;i--){
        update(1,1,i,-a[i+1]);
        if(a[i]>=0)ans2[i]=query(1,L[i],i);
        else ans2[i]=query1(1,L[i],i);
    }
    long long mx=-0x3f3f3f3f3f3f3f3f;
    for(int i=1;i<=n;i++){
        mx=max(mx,(ans1[i]+ans2[i]-a[i])*a[i]);
    }
    printf("%lld\n",mx);
    return 0;
}

Problem J

题意:

给你一棵有nn个节点的树,每条边上有个边权ww,现在有qq个询问,问你在节点uu到节点vv的路径中,有多少条边的边权是大于kk的。

分析:

考虑去二分答案。如果用二分去解的话,这个问题就转化为让你找到一个最小的tt,使得tt条边的权值都大于kk。则显然我们只需要求这段路径上的第tt大的路径的值,并判断第tt大的路径的值是否大于kk即可。

因此我们要求的就是一个经典的树上路径第kk大的问题,因此我们可以用主席树去解决。

总体的时间复杂度为:O(nlog2(n))\mathcal{O}(nlog^2(n))

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

const int MAXN = 200010;
const int M = MAXN * 40;
int n,q,m,TOT;
int a[MAXN], t[MAXN];
int T[M], lson[M], rson[M], c[M];

void Init_hash()
{
    for(int i = 1; i <= n;i++)
        t[i] = a[i];
    sort(t+1,t+1+n);
    m = unique(t+1,t+n+1)-t-1;
}
int build(int l,int r)
{
    int root = TOT++;
    c[root] = 0;
    if(l != r)
    {
        int mid = (l+r)>>1;
        lson[root] = build(l,mid);
        rson[root] = build(mid+1,r);
    }
    return root;
}
int Hash(int x)
{
    return lower_bound(t+1,t+1+m,x) - t;
}
int update(int root,int pos,int val)
{
    int newroot = TOT++, tmp = newroot;
    c[newroot] = c[root] + val;
    int l = 1, r = m;
    while( l < r)
    {
        int mid = (l+r)>>1;
        if(pos <= mid)
        {
            lson[newroot] = TOT++; rson[newroot] = rson[root];
            newroot = lson[newroot]; root = lson[root];
            r = mid;
        }
        else
        {
            rson[newroot] = TOT++; lson[newroot] = lson[root];
            newroot = rson[newroot]; root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;
    }
    return tmp;
}
int query(int left_root,int right_root,int LCA,int k)
{
    int lca_root = T[LCA];
    int pos = Hash(a[LCA]);
    int l = 1, r = m;
    while(l < r)
    {
        int mid = (l+r)>>1;
        int tmp = c[lson[left_root]] + c[lson[right_root]] - 2*c[lson[lca_root]] + (pos >= l && pos <= mid);
        if(tmp >= k)
        {
            left_root = lson[left_root];
            right_root = lson[right_root];
            lca_root = lson[lca_root];
            r = mid;
        }
        else
        {
            k -= tmp;
            left_root = rson[left_root];
            right_root = rson[right_root];
            lca_root = rson[lca_root];
            l = mid + 1;
        }
    }
    return l;
}

int rmq[2*MAXN];
struct ST
{
    int mm[2*MAXN];
    int dp[2*MAXN][20];
    void init(int n)
    {
        mm[0] = -1;
        for(int i = 1;i <= n;i++)
        {
            mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
            dp[i][0] = i;
        }
        for(int j = 1; j <= mm[n];j++)
            for(int i = 1; i + (1<<j) - 1 <= n; i++)
                dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
    }
    int query(int a,int b)
    {
        if(a > b)swap(a,b);
        int k = mm[b-a+1];
        return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
    }
};
struct Edge
{
    int to,next,val;
};
Edge edge[MAXN*2];
int tot,head[MAXN];

int F[MAXN*2];
int P[MAXN];
int cnt;

ST st;
void init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int val)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].val=val;
    head[u] = tot++;
}
void dfs(int u,int pre,int dep)
{
    F[++cnt] = u;
    rmq[cnt] = dep;
    P[u] = cnt;
    for(int i = head[u];i != -1;i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == pre)continue;
        dfs(v,u,dep+1);
        F[++cnt] = u;
        rmq[cnt] = dep;
    }
}
void LCA_init(int root,int node_num)
{
    cnt = 0;
    dfs(root,root,0);
    st.init(2*node_num-1);
}
int query_lca(int u,int v)
{
    return F[st.query(P[u],P[v])];
}

void dfs_build(int u,int pre)
{
    int pos = Hash(a[u]);
    T[u] = update(T[pre],pos,1);
    for(int i = head[u]; i != -1;i = edge[i].next)
    {
        int v = edge[i].to;
        if(v == pre)continue;
        dfs_build(v,u);
    }
}
bool check(int u,int v,int k,int val){
    if(t[query(T[u],T[v],query_lca(u,v),k)]>val) return 1;
    return 0;
}
int Find(int u,int v,int l,int r,int k){
    int ret;
    while(l<=r){
        int mid=(l+r)>>1;
        if(check(u,v,mid,k)){
            ret=mid;
            r=mid-1;
        }else l=mid+1;
    }
    if(a[query_lca(u,v)]<=k) ret-=1;
    return ret;
}
void dfs(int u,int fa){
    for(int i=head[u];~i;i=edge[i].next){
        int to=edge[i].to;
        if(to==fa) continue;
        a[to]=edge[i].val;
        dfs(to,u);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&n,&q) == 2)
    {

        TOT = 0;
        int u,v,vv;
        a[1]=0x3f3f3f3f;
        init();
        for(int i = 1;i < n;i++)
        {
            scanf("%d%d%d",&u,&v,&vv);
            addedge(u,v,vv);
            addedge(v,u,vv);
        }
        dfs(1,-1);
        Init_hash();
        LCA_init(1,n);
        T[n+1] = build(1,m);
        dfs_build(1,n+1);
        int k;
        while(q--)
        {
            scanf("%d%d%d",&u,&v,&k);
            printf("%d\n",Find(u,v,1,n,k)-1);
        }
        return 0;
    }
    return 0;
}

Problem K

找规律+前缀亦或和

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=1e5+7;
unsigned int a[maxn],b[maxn][4];
namespace fastIO {
#define BUF_SIZE 100000
    //fread -> read
    bool IOerror = 0;
    inline char nc() {
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
        if(p1 == pend) {
            p1 = buf;
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pend == p1) {
                IOerror = 1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) {
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
    }
    inline void read(int &x) {
        char ch;
        while(blank(ch = nc()));
        if(IOerror) return;
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
    }
    inline void read(unsigned int &x) {
        char ch;
        while(blank(ch = nc()));
        if(IOerror) return;
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
    }
#undef BUF_SIZE
};
using namespace fastIO;
int main(){
    int t;
    read(t);
    while (t--){
        int n,q,l,r;
        read(n);
        for (int i=0;i<n;i++) {
            read(a[i]);
            b[i][0]=b[i][1]=b[i][2]=b[i][3]=0;
        }
        int m=(n+3)/4+1;
        for (int i=0;i<n;i++)
            b[i/4+1][i%4]=a[i];
//        for (int i=0;i<4;i++){
//            for (int j=0;j<=m+1;j++) cout<<b[j][i]<<' ';cout<<endl;
//        }

        for (int i=1;i<=m+1;i++) {
            for (int j=0;j<4;j++)
                b[i][j]^=b[i-1][j];
        }
        read(q);
        while (q--){
            read(l);read(r);
            unsigned ans=0;
            int tmp=(r-l)%4;
            l--;r--;
            if (tmp==3) ans=0;
            else if (tmp==2) {
                int ll=l+1,rr=r-1;
                ans=b[rr/4+1][ll%4]^b[ll/4][ll%4];
            }else if (tmp==1){
                int ll=l,rr=r-1;
                ans=b[rr/4+1][ll%4]^b[ll/4][ll%4] ^b[(rr+1)/4+1][(ll+1)%4]^b[(ll+1)/4][(ll+1)%4];
            }else if (tmp==0){
                int ll=l,rr=r;
                ans=b[rr/4+1][ll%4]^b[ll/4][ll%4];
            }
            printf("%u\n",ans);
        }
    }
    return 0;
}

Problem L


Problem M

题意:

给你一个串strstr以及nn个串stristr_i,问你这nn个串stristr_i是否是串strstr的子序列。

分析:

直接暴力匹配显然会T,而又根据子序列的性质(下一个字符要匹配的字符cc必定是在原串离上一个匹配成果的位置pospos的最近的位置上),我们可以考虑对原串进行预处理。

我们设f[i][j]\text{f[i][j]}为:在原串ii位置上,离字符jj最近的位置。我们可以用O(len)\mathcal{O}(len)的时间预处理出来这个,而在我们进行nn个串的匹配时,我们不断在ff数组上去跳转即可。

代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
char str[100005],p[1005];
int f[100005][300];
int len,n;
void init(){
    for(int i=0;i<len;i++){
        int flag=str[i];
        for(int j=i;j>=0;j--){
            if(f[j][flag]!=-1) break;
            f[j][flag]=i;
        }
    }
}
int main()
{
//freopen("in.txt","r",stdin);
    scanf("%s",str);
    scanf("%d",&n);
    len=strlen(str);
    memset(f,-1,sizeof(f));
    init();
    while(n--){
        scanf("%s",p);
        int l=strlen(p);
        int cur=0;
        bool flag=0;
        for(int i=0;i<l;i++){
            if(f[cur][p[i]]==-1){
                flag=1;
                break;
            }
            cur=f[cur][p[i]]+1;
        }
        if(flag)puts("NO");
        else puts("YES");
    }
    return 0;
}
posted @ 2019-04-22 12:17  ChenJr  阅读(146)  评论(0编辑  收藏  举报