1. 程式人生 > >2017 CCPC 杭州賽區

2017 CCPC 杭州賽區

題面:http://acm.hdu.edu.cn/downloads/CCPC2018-Hangzhou-ProblemSet.pdf

Problem A. Super-palindrome:

每個奇數長度的子串都是迴文,有兩種情況:

1. aaaaaaa 都是相同字元

2.ababababab 兩個不同的字元一直交替。

列舉每兩個字元即可。

程式碼:

#include<bits/stdc++.h>
using namespace std;
char t[105];
int k[27];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%s",t+1);
        int len=strlen(t+1),ans=1e9;
        for(int i=0;i<26;i++)
        {
            for(int j=0;j<26;j++)
            {
                int num=0;
                for(int k=1;k<=len;k++)
                {
                    if(k%2&&t[k]-'a'!=i) num++;
                    if(k%2==0&&t[k]-'a'!=j) num++;
                }
                ans=min(ans,num);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

Problem B. Master of Phi:

把尤拉函式φ(d)=d*\prod p/n(1-1/p)帶入到上式得  \prod p|d(1-1/p)

d可以寫成p1^(x1) * p2^(x2) * p3^(x3) ..... 把xi==0看做一種狀態,xi>0看做一種狀態,

則一共有2^(20)種狀態,每種狀態的\prod p|d(1-1/p)值相同,對於每種狀態的每個不為0的

xi,都有1~qi種選擇,因此每種狀態有所有不為0的qi之積。

程式碼:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll p[22],q[22],inv[22],ans;
int m;
ll pow1(ll a,ll b)
{
    ll r=1;
    while(b)
    {
        if(b&1) r=r*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return r;
}
void dfs(int i,ll cnt)
{
    if(i==m+1)
    {
        ans=(ans+cnt)%mod;
        return;
    }
    dfs(i+1,cnt);
    dfs(i+1,cnt*q[i]%mod*(p[i]-1)%mod*inv[i]%mod);
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&m);
        ll n=1;
        ans=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%lld%lld",&p[i],&q[i]);
            n=n*pow1(p[i],q[i])%mod;
            inv[i]=pow1(p[i],mod-2);
        }
        dfs(1,n);
        printf("%lld\n",ans);
    }
    return 0;
}

Problem C. Hakase and Nano:

若H先手,當且僅當n%3==1,且所有數都為1時H才會輸。

對應,H後手時只有Nano能把狀態轉化成n%3==n,且每個數都為1才會獲勝。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+10;
int a[maxn];
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n,d,ans=0;scanf("%d%d",&n,&d);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]>1) ans++;
        }
        if(d==1)
        {
            if(n<=2) printf("Yes\n");
            else if(n%3==0&&ans==0) printf("No\n");
            else printf("Yes\n");
        }
        else
        {
            if(n==1) printf("No\n");
            else if(n==2) printf("Yes\n");
            else if(n%3==1&&ans<=1) printf("No\n");
            else if(n%3==0&&ans==1) printf("No\n");
            else printf("Yes\n");
        }
    }
    return 0;
}

Problem D. Master of Random:

對於每個i,所有的j>i,都有1/i的概率在以i為根的子樹內,計算每個點的期望然後求平均即可。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const ll mod=998244353;
ll a[maxn];
ll pow1(ll a,ll b)
{
    ll r=1;
    while(b)
    {
        if(b&1) r=r*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return r;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        ll sum=0,ans=0;
        for(int i=n;i>=1;i--)
        {
            ans+=sum*pow1(1LL*i,mod-2)%mod;
            (ans+=a[i])%=mod;
            (sum+=a[i])%=mod;
        }
        cout<<ans*pow1(1LL*n,mod-2)%mod<<endl;
    }
    return 0;
}

Problem E. Master of Subgraph:

考慮以一個點為根,且必須選根的方案,選擇一個點則必須選擇這個點的父親節點,因此此節點的狀態就是

他的父節點S[fa]<<w[i],回溯時把子節點的狀態新增到父節點中S[fa] | =S[i]。然後點分治處理。

複雜度n*m*logn,因為是0,1,狀態的轉化,因此可以用bitset優化轉移。

程式碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
bitset<maxn>S[3003],ans;
vector<int>G[3003];
int son[3003],root,now_size;
int w[3003],n,m;
bool vis[3003];
void get_root(int v,int fa,int SIZE)
{
    son[v]=1;
    int ma=0;
    for(int i=0;i<G[v].size();i++)
    {
        int to=G[v][i];
        if(to==fa||vis[to]) continue;
        get_root(to,v,SIZE);
        son[v]+=son[to];
        ma=max(ma,son[to]);
    }
    ma=max(ma,SIZE-son[v]);
    if(ma<now_size)
    {
        now_size=ma;
        root=v;
    }
}
void cal(int v,int fa)
{
    for(int i=0;i<G[v].size();i++)
    {
        int to=G[v][i];
        if(to==fa||vis[to]) continue;
        S[to]=(S[v]<<w[to]);
        cal(to,v);
        S[v]|=S[to];
    }
}
void dfs(int v,int fa)
{
    vis[v]=1;
    S[v].reset();
    S[v][w[v]]=1;
    cal(v,0);
    ans|=S[v];
    for(int i=0;i<G[v].size();i++)
    {
        int to=G[v][i];
        if(vis[to]) continue;
        get_root(root=to,0,now_size=son[to]);
        dfs(root,0);
    }
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        for(int i=0;i<3003;i++) G[i].clear();
        memset(vis,0,sizeof(vis));
        ans.reset();
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;i++)
        {
            int x,y;scanf("%d%d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        for(int i=1;i<=n;i++) scanf("%d",&w[i]);
        get_root(root=1,0,now_size=n);
        dfs(root,0);
        for(int i=1;i<=m;i++)
        {
            if(ans[i]) printf("1");
            else printf("0");
        }
        printf("\n");
    }
    return 0;
}

Problem J. Master of GCD:

這題上來第一眼想線段樹區間乘法,但是每次乘2最後數很大需要取模,但是先取模後求GCD和先求GCD後取模結果不同。

考慮到最後n個數都可以寫成(2^x)*(3^y)的形式。取n個數中最小的x和y,設為xx,yy,則(2^xx)*(3^yy)便是所有數的最大公約數。

剩下的便是線段樹維護區間加法。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
const ll mod=998244353;
struct node
{
    ll v1,v2,lz1,lz2;
}tree[maxn<<2];
ll pow1(ll a,ll b)
{
    ll r=1;
    while(b)
    {
        if(b&1) r=r*a%mod;
        a=a*a%mod;
        b/=2;
    }
    return r;
}
void push_down(int l,int r,int rt)
{
    int mid=(l+r)/2;
    if(tree[rt].lz1)
    {
        tree[rt<<1].v1+=(mid-l+1)*tree[rt].lz1;
        tree[rt<<1|1].v1+=(r-mid)*tree[rt].lz1;
        tree[rt<<1].lz1+=tree[rt].lz1;
        tree[rt<<1|1].lz1+=tree[rt].lz1;
        tree[rt].lz1=0;
    }
    if(tree[rt].lz2)
    {
        tree[rt<<1].v2+=(mid-l+1)*tree[rt].lz2;
        tree[rt<<1|1].v2+=(r-mid)*tree[rt].lz2;
        tree[rt<<1].lz2+=tree[rt].lz2;
        tree[rt<<1|1].lz2+=tree[rt].lz2;
        tree[rt].lz2=0;
    }
}
void update(int L,int R,int C,int l,int r,int rt)
{
    if(l>=L&&r<=R)
    {
        if(C==2)
        {
            tree[rt].v1+=(r-l+1);
            tree[rt].lz1+=1;
        }
        else
        {
            tree[rt].v2+=(r-l+1);
            tree[rt].lz2+=1;
        }
        return;
    }
    push_down(l,r,rt);
    int mid=(l+r)/2;
    if(L<=mid) update(L,R,C,l,mid,rt<<1);
    if(R>mid) update(L,R,C,mid+1,r,rt<<1|1);
}
node query(int L,int l,int r,int rt)
{
    if(l==r) return tree[rt];
    push_down(l,r,rt);
    int mid=(l+r)/2;
    if(L<=mid) return query(L,l,mid,rt<<1);
    else return query(L,mid+1,r,rt<<1|1);
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        for(int i=0;i<4*maxn;i++)
            tree[i].v1=tree[i].v2=tree[i].lz1=tree[i].lz2=0;
        int n,m;scanf("%d%d",&n,&m);
        while(m--)
        {
            int l,r,k;scanf("%d%d%d",&l,&r,&k);
            update(l,r,k,1,n,1);
        }
        ll num1=1e8,num2=1e8;
        for(int i=1;i<=n;i++)
        {
            node e=query(i,1,n,1);
            num1=min(num1,e.v1);
            num2=min(num2,e.v2);
            //printf("%lld %lld\n",e.v1,e.v2);
        }
        ll ans=pow1(2,num1)*pow1(3,num2)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

Problem K. Master of Sequence:

a[i]只有1000,可以按a[i]歸類,f[i][j]表示a[i]==i,且b[i]%a[i]>=j的有多少個。

ret=\sum b[i]/a[i],   - ret 即為當前t==0時的值,二分t,cnt=\sum t/a[i],

然後對於每個 t%a[i]<b[i]%a[i],cnt=cnt-1;

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
ll a[maxn],b[maxn],f[1005][1005];
ll ans,len[1005],ret;
int n,m;
bool check(ll x,ll k)
{
    ll cnt=0;
    for(int i=1;i<=1000;i++)
    {
        if(!len[i]) continue;
        cnt+=f[i][0]*(x/i);
        cnt-=f[i][x%i+1];
    }
    if(cnt-ret>=k) return 1;
    return 0;
}
ll solve(ll k)
{
    ll l=1,r=1e13,cnt;
    while(l<=r)
    {
        ll mid=(l+r)/2;
        if(check(mid,k)) cnt=mid,r=mid-1;
        else l=mid+1;
    }
    return cnt;
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        memset(len,0,sizeof(len));
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
        ret=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&b[i]);
            len[a[i]]++; f[a[i]][b[i]%a[i]]++;
            ret+=b[i]/a[i];
        }
        for(int i=1;i<=1000;i++)
            for(int j=i-1;j>=0;j--)
            f[i][j]+=f[i][j+1];
        while(m--)
        {
            int op,x,y,z;scanf("%d",&op);
            if(op==1)
            {
                scanf("%d%d",&x,&y);
                for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]--;
                len[a[x]]--;
                ret-=b[x]/a[x];
                a[x]=y;
                len[a[x]]++;
                ret+=b[x]/a[x];
                for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]++;
            }
            else if(op==2)
            {
                scanf("%d%d",&x,&y);
                for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]--;
                ret-=b[x]/a[x];
                b[x]=y;
                ret+=b[x]/a[x];
                for(int i=b[x]%a[x];i>=0;i--) f[a[x]][i]++;
            }
            else
            {
                scanf("%d",&z);
                printf("%lld\n",solve(1LL*z));
            }
        }
    }
    return 0;
}