CCPC-Wannafly Winter Camp Day7 (Div2, onsite)

目录

 

A.迷宫

C.斐波那契数列

E.线性探查法

F.逆序对!

G.抢红包机器人


A.迷宫

相当于若干条链合并成一条长链,合并的时候如果有冲突放在后面的空位上。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
struct Edge{
    int v,nxt;
    Edge(int v=0,int nxt=0):v(v),nxt(nxt){}
}e[N*2];
int p[N],edn,n;
void add(int u,int v){
    e[++edn]=Edge(v,p[u]);p[u]=edn;
    e[++edn]=Edge(u,p[v]);p[v]=edn;
}
int d[N];
int dfs(int u,int f,int de){
    d[u]=de;
    for(int i=p[u];~i;i=e[i].nxt){
        int v=e[i].v;
        if(v==f) continue;
        dfs(v,u,de+1);
    }
}
int cnt[N],a[N];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    int u,v;
    memset(p,-1,sizeof(p));edn=-1;
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    dfs(1,-1,0);
    for(int i=1;i<=n;i++){
        if(a[i]) cnt[d[i]]++;
    }
    int ans=0;
    for(int i=1;i<=n;i++){
        if(cnt[i]){
            if(i>ans) ans=i-1;
            ans+=cnt[i];
        }
    }
    printf("%d\n",ans);
}

C.斐波那契数列

Fib(n)&(Fib(n)-1)=Fib(n)-lowbit(n),所以原式等于\sum _{n=1}^{R}Fib_{n}-\sum _{n=1}^{R}lowbit(n).

斐波那契前n项和为sum[n]=F(n+2)-1,所以前者可以用矩阵快速幂来计算。

根据洪华敦老师的结论:

  • 只有n是3的倍数时,F[n]才是偶数
  • 当n=3k时,若k是奇数,则lowbit(F[n])=2,否则lowbit(F[n])=lowbit(4k)

所以我们只需要计算n为6的倍数的情况,也就是要算出\sum _{n=1}^{N/6}lowbit(n),这可以通过枚举二进制位来计算。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e3+7;
const ll mod=998244353;
void mul(ll a[2][2],ll b[2][2]){
    ll c[2][2];
    for(ll i=0;i<2;i++){
        for(ll j=0;j<2;j++){
            c[i][j]=0;
            for(ll k=0;k<2;k++){
                c[i][j]=(c[i][j]+a[i][k]*b[k][j]%mod)%mod;
            }
        }
    }
    memcpy(a,c,sizeof(c));
}
void qpow(ll a[2][2],ll p[2][2],ll b){
    while(b){
        if(b&1) mul(a,p);
        mul(p,p);b>>=1;
    }
}
ll solve(ll x){
    ll res=0;
    for(ll i=0;(1ll<<i)<=x;i++){
        if(x&(1ll<<i)) res=(res+((x>>(i+1))+1)*(1ll<<i))%mod;
        else res=(res+(x>>(i+1))*(1ll<<i))%mod;
    }
    return res;
}
ll de[6]={0,1,2,4,5,6};
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        ll n;
        scanf("%lld",&n);
        ll a[2][2]={1,1,0,0};
        ll p[2][2]={0,1,1,1};
        qpow(a,p,n+1);
        ll ans=(a[0][0]-1+mod)%mod;
        ll tmp=n/6;
        ans=(ans-tmp*6%mod+mod)%mod;
        ans=(ans-8*solve(tmp)+mod)%mod;
        ans=(ans-de[n%6]+mod)%mod;
        printf("%lld\n",ans);
    }
}

E.线性探查法

按照题意建树,然后拓扑排序。

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+7;
int b[N],n;
int p[N],edn;
int d[N];
struct Edge{
    int v,nxt;
    Edge(int v=0,int nxt=0):v(v),nxt(nxt){}
}e[N*N];
void add(int u,int v){
    e[++edn]=Edge(v,p[u]);p[u]=edn;
}
struct Node{
    int id,val;
    Node(int id,int val):id(id),val(val){}
    bool operator<(const Node x)const{
        return val>x.val;
    }
};
priority_queue<Node>q;
int tot;
int tmp[N];
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&b[i]);
    }
    memset(p,-1,sizeof(p));edn=-1;
    for(int i=0;i<n;i++){
        tot=0;
        int st=i;
        if((b[i]%n)!=i){
            for(int j=(i+1)%n;j!=i;j=(j+1)%n){
                if(b[j]%n==i){
                    st=j;break;
                }
            }
            if(st==i) continue;
            tmp[++tot]=st;
        }
        for(int j=(st+1)%n;j!=st;j=(j+1)%n){
            if(b[j]%n==i) tmp[++tot]=j;
        }
        if(tot==0) continue;
        int pos=1;
        add(i,tmp[pos]);d[tmp[pos]]++;
        for(int j=(i+1)%n;j!=i;j=(j+1)%n){
            if(j!=tmp[pos]){
                add(j,tmp[pos]);
                d[tmp[pos]]++;
            }
            else{
                pos++;j--;
                if(pos>tot) break;
            }
        }
    }
    for(int i=0;i<n;i++){
        if(d[i]==0) q.push(Node(i,b[i]));
    }
    bool fst=true;
    while(!q.empty()){
        Node t=q.top();q.pop();
        if(fst) fst=false;
        else printf(" ");
        printf("%d",t.val);
        for(int i=p[t.id];~i;i=e[i].nxt){
            int v=e[i].v;
            d[v]--;
            if(d[v]==0) q.push(Node(v,b[v]));
        }
    }
    printf("\n");
}

F.逆序对!

对于两个数字a和b,假设a和b二进制最高的不同位是第k位。当两者同时异或一个c,且c在第k位的值为1,则a和b异或c会改变两者的大小关系。

对于本题n^2枚举两个数a[i],a[j] (i < j),计算这两个数对答案的贡献。

  • 如果a[i]<a[j],则必须异或一个第k位为1的值才有贡献
  • 如果a[i]>a[j],则必须异或一个第k位为0的值才有贡献

第k位为1或0可以乘法O(1)来计算。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e3+7;
const ll mod=998244353;
ll b[N],c[50];
ll n,m;
ll solve(ll x,ll y){
    bool xx,yy;
    for(ll i=30;i>=0;i--){
        if(x&(1<<i)) xx=true;
        else xx=false;
        if(y&(1<<i)) yy=true;
        else yy=false;
        if(xx!=yy) return i;
    }
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=0;i<=30;i++){
        c[i]=(1<<i)*(m>>(i+1))%mod;
        if(m&(1<<i)) c[i]=(c[i]+(m&((1<<i)-1))+1)%mod;
    }
    ll ans=0;
    for(ll i=1;i<=n;i++){
        scanf("%lld",&b[i]);
        for(ll j=1;j<i;j++){
            ll p=solve(b[j],b[i]);
            if(b[j]<b[i]) ans=(ans+c[p])%mod;
            else ans=(ans+m-c[p]+mod)%mod;
        }
    }
    printf("%lld\n",ans);
}

G.抢红包机器人

按照题意建树,枚举机器人暴力搜索,取最小值

#include<bits/stdc++.h>
using namespace std;
const int N=1e2+7;
int n,m,num;
int mp[N][N];
bool vis[N];
int tt[N];
int bfs(int s){
    queue<int>q;
    int res=1;
    q.push(s);vis[s]=true;
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int v=1;v<=n;v++){
            if(mp[u][v]&&!vis[v]){
                q.push(v);res++;
                vis[v]=true;
            }
        }
    }
    return res;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++){
        scanf("%d",&num);
        for(int j=1;j<=num;j++){
            scanf("%d",&tt[j]);
            if(j!=1){
                mp[tt[j]][tt[j-1]]=1;
            }
        }
    }
    int ans=n;
    for(int i=1;i<=n;i++){
        memset(vis,false,sizeof(vis));
        ans=min(ans,bfs(i));
    }
    printf("%d\n",ans);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值