省选联测 42

又垫底了。不懂为什么 T3 都切了。鉴定为人菜。

joke3579 说他演了一整场,那他比较强。

猜数字

思博题。位数是 \(n\lg n+1\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
int n;
char s[300010];
int main(){
    scanf("%s",s+1);n=strlen(s+1);
    if(n==1){
        if(s[1]=='1')puts("1");
        else puts("2");
        return 0;
    }
    for(int i=3;i<=50000;i++){
        if((int)(i*log10(i)+1)==n){
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}

吵架

原题。ZJOI 捉迷藏。就是线段树找直径。

赛时一开始调了半天点分树。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int n,q;
struct gra{
    int v,next;
}edge[200010];
int t,head[100010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int size[100010],top[100010],dep[100010],son[100010],fa[100010];
void dfs1(int x,int f){
    size[x]=1;fa[x]=f;dep[x]=dep[f]+1;
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f){
            dfs1(edge[i].v,x);
            size[x]+=size[edge[i].v];
            if(size[son[x]]<size[edge[i].v])son[x]=edge[i].v;
        }
    }
}
void dfs2(int x,int f,int tp){
    top[x]=tp;
    if(son[x])dfs2(son[x],x,tp);
    for(int i=head[x];i;i=edge[i].next){
        if(edge[i].v!=f&&edge[i].v!=son[x])dfs2(edge[i].v,x,edge[i].v);
    }
}
int lca(int x,int y){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    return x;
}
int dis(int x,int y){
    if(!x||!y)return -1;
    return dep[x]+dep[y]-2*dep[lca(x,y)];
}
int col[100010];
#define lson rt<<1
#define rson rt<<1|1
struct stu{
    int d,x,y;
    friend stu operator+(const stu&s,const stu&t){
        if(s.d==0&&t.d==0)return {dis(s.x,t.x),s.x,t.x};
        else if(s.d==-1)return t;
        else if(t.d==-1)return s;
        stu ans={0,0,0};
        if(ans.d<s.d)ans=s;
        if(ans.d<t.d)ans=t;
        int d=dis(s.x,t.x);
        if(ans.d<d)ans={d,s.x,t.x};
        d=dis(s.x,t.y);
        if(ans.d<d)ans={d,s.x,t.y};
        d=dis(s.y,t.x);
        if(ans.d<d)ans={d,s.y,t.x};
        d=dis(s.y,t.y);
        if(ans.d<d)ans={d,s.y,t.y};
        return ans;
    }
}tree[400010];
void build(int rt,int l,int r){
    if(l==r){
        col[l]=true;tree[rt]={0,l,0};return;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    tree[rt]=tree[lson]+tree[rson];
}
void update(int rt,int L,int R,int pos){
    if(L==R){
        if(col[pos])tree[rt]={-1,0,0},col[pos]=false;
        else tree[rt]={0,pos,0},col[pos]=true;
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)update(lson,L,mid,pos);
    else update(rson,mid+1,R,pos);
    tree[rt]=tree[lson]+tree[rson];
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        add(u,v);add(v,u);
    }
    dfs1(1,0);dfs2(1,0,1);
    build(1,1,n);
    cin>>q;
    while(q--){
        char od[5];cin>>od;
        if(od[0]=='C'){
            int x;cin>>x;update(1,1,n,x);
        }
        else cout<<tree[1].d<<'\n';
    }
    return 0;
}

选数游戏 V2

赛时冲了一发 floyd。

首先特判一堆东西。然后问题变成每个数二进制位 \(1\)\(2\)\(1\),找最少的数使得异或和为 \(0\)。然后对于两个 \(1\) 的在它们之间连边,对于一个 \(1\) 的新建个虚点 \(0\) 连边,就变成了最小环。显然不能 floyd,是 \(O(n^3)\)。暴力枚举每个点为起点也是 \(O(n^2)\)

发现每个环一定经过小于 \(1000\) 的质因数,于是只需要扫这些点作为起点,就可以过了。复杂度 \(O(n\pi(\sqrt n))\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
int n,fac[10],ans=0x3f3f3f3f;
struct node{
    int v,next;
}edge[200010];
int t,head[1000010];
void add(int u,int v){
    edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int p[1010];
bool v[1010];
void get(int n=1000){
    for(int i=2;i<=n;i++){
        if(!v[i])p[++p[0]]=i;
        for(int j=1;j<=p[0]&&i*p[j]<=n;j++){
            v[i*p[j]]=true;
            if(i%p[j]==0)break;
        }
    }
}
int dis[1000010];
bool vis[1000010];
void bfs(int st){
    queue<int>q;
    for(int i=1;i<=1000000;i++)dis[i]=0x3f3f3f3f,vis[i]=false;
    dis[st]=0;q.push(st);
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=true;
        for(int i=head[x];i;i=edge[i].next){
            if(vis[edge[i].v])continue;
            if(dis[edge[i].v]==0x3f3f3f3f){
                dis[edge[i].v]=dis[x]+1,q.push(edge[i].v);
            }
            else ans=min(ans,dis[x]+dis[edge[i].v]+1);
        }
    }
}
int main(){
    scanf("%d",&n);get();
    for(int i=1;i<=n;i++){
        int x;scanf("%d",&x);
        if(x==1){
            puts("1");return 0;
        }
        fac[0]=0;fac[2]=1;
        for(int j=2;j*j<=x;j++){
            int cnt=0;
            while(x%j==0)cnt++,x/=j;
            if(cnt&1)fac[++fac[0]]=j;
        }
        if(x>1)fac[++fac[0]]=x;
        add(fac[1],fac[2]);add(fac[2],fac[1]);
    }
    for(int i=1;i<=p[0];i++)bfs(p[i]);
    if(ans==0x3f3f3f3f)ans=-1;
    printf("%d\n",ans);
}

nnntxdy

无数学。差评。不过 dp 真的好玄妙。我果然永远不会 dp。

首先看着这个 \(n\) 就很状压。那么设 \(dp_{i,S}\) 为克苏恩射 \(i\) 发还剩下 \(S\) 内的活着的概率。考虑转移,两种情况:

  1. \(i\) 发射死一个:首先能分配的总伤害是射了 \(i-1\) 发减去射死的总血量,需要的伤害是这个随从的血量 \(-1\),是个组合数。不要忘记乘 \(1/\) 活着的随从数量。
  2. 没射死:需要能分配的伤害能够给每个随从留下最少一滴血。也要乘 \(1/\) 活着的随从数量。

然后考虑如何计算答案。对于活着的集合 \(s\),贡献是显然的,看看怎么算概率。由于我们还没有分配没射死随从的伤害,要考虑一下这些方案。那么分别扫每个活着的随从,乘若干组合数即可。具体的,设个 \(cnt_i\) 为还剩下 \(i\) 点伤害没分配的方案数,要的就是 \(cnt_0\)。枚举每个活着的随从受到多少伤害,每次乘组合数。

反正你要是让我再做一遍我也不会。qnmddp。

(克苏恩为什么不打脸)(算了反正炉石似了)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int mod=998244353;
int n,m,ans,a[20],C[210][210],dp[210][1<<15],inv[210],cnt[210];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    inv[1]=1;
    for(int i=2;i<=200;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=0;i<=200;i++){
        C[i][0]=1;
        for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    dp[0][(1<<n)-1]=1;
    for(int i=0;i<m;i++){
        for(int s=1;s<(1<<n);s++){
            if(!dp[i][s])continue;
            int ret=i,sum=0;
            for(int j=1;j<=n;j++){
                if((s>>j-1)&1)sum+=a[j];
                else ret-=a[j];
            }
            for(int j=1;j<=n;j++){
                if(((s>>j-1)&1)&&ret>=a[j]-1)dp[i+1][s^(1<<j-1)]=(dp[i+1][s^(1<<j-1)]+1ll*dp[i][s]*inv[__builtin_popcount(s)]%mod*C[ret][a[j]-1])%mod;
            }
            if(ret<sum-__builtin_popcount(s))dp[i+1][s]=(dp[i+1][s]+1ll*dp[i][s]*inv[__builtin_popcount(s)])%mod;
        }
    }
    for(int s=0;s<(1<<n);s++){
        if(!dp[m][s])continue;
        int ret=m,sum=0;
        for(int i=1;i<=n;i++){
            if((s>>i-1)&1)sum+=a[i];
            else ret-=a[i];
        }
        cnt[ret]=1;
        for(int i=0;i<ret;i++)cnt[i]=0;
        for(int i=1;i<=n;i++){
            if(!((s>>i-1)&1))continue;
            for(int j=1;j<=ret;j++){
                for(int k=1;k<=min(a[i]-1,j);k++)cnt[j-k]=(cnt[j-k]+1ll*cnt[j]*C[j][k])%mod;
            }
        }
        ans=(ans+1ll*(n-__builtin_popcount(s))*dp[m][s]%mod*cnt[0])%mod;
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2023-02-28 15:43  gtm1514  阅读(41)  评论(0编辑  收藏  举报