1. 程式人生 > >10.15離線賽

10.15離線賽

一、小X的質數
資料:
這裡寫圖片描述
計蒜客原題

就是篩一下素數,然後按照題意就行了。1e7以內有60多萬個素數,直接n^2豈不是超時?素數相乘會很大,當大於R時break掉就行了。然後再一個字首和維護一下就沒了。

Code:

#include<bits/stdc++.h>
using namespace std;
int prm[665005],n,pc,cnt[10000005];
bool Q[10000005];
void Creat(int x){//篩素數
    for(int i=2;i<=x;i++){
        if(Q[i])continue;
        prm[++pc]=i;cnt[i]=1
; for(int j=i;j<=x;j+=i)Q[j]=1; } } struct node{int l,r;}A[100005]; int main(){ scanf("%d",&n); int mx=0; for(int i=1;i<=n;i++){ scanf("%d%d",&A[i].l,&A[i].r); if(A[i].r>mx)mx=A[i].r;//找到最大的邊界 } Creat(mx); for(int i=1;i<=pc;i++) for
(int j=i;j<=pc&&1LL*prm[i]*prm[j]<=1LL*mx;j++) cnt[prm[i]*prm[j]]=1;//太大了就不算了,這樣就只算了190多萬次 for(int i=1;i<=mx;i++)cnt[i]+=cnt[i-1]; for(int i=1;i<=n;i++)printf("%d\n",cnt[A[i].r]-cnt[A[i].l-1]); return 0; }

二、小X的密室
資料:
這裡寫圖片描述
計蒜客原題

第一反應是部分分,沒有鑰匙就只是一個最短路。然後這50分我拿了45分……不然我三百來著……
然後想到的是dp,因為看到鑰匙只有10,。然後又想了一下,發現dp也寫不了,沒有一個順序。然後就想到了廣搜。判重就只用記錄一下房間和鑰匙數量就行了。然後就沒了。

Code:

#include<bits/stdc++.h>
using namespace std;
int key[5005],n,m,K;
struct node{int to,k;};
vector<node>edge[5005];
struct node1{int x,k,step;};
queue<node1>Q;
bool mark[5005][1030];
int f(){
    Q.push((node1){1,key[1],0});
    mark[1][key[1]]=1;
    while(!Q.empty()){
        node1 now=Q.front();Q.pop();
        int x=now.x,k1=now.k;
        for(int i=0;i<(int)edge[x].size();i++)
            if((k1&edge[x][i].k)==edge[x][i].k){
                node1 nxt;
                nxt.x=edge[x][i].to;
                nxt.k=k1|key[edge[x][i].to];
                nxt.step=now.step+1;
                if(mark[nxt.x][nxt.k])continue;
                mark[nxt.x][nxt.k]=1;
                if(nxt.x==n)return nxt.step;
                Q.push(nxt);
            }
    }
    return -1;
}
int main(){
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=n;i++){
        for(int j=0;j<K;j++){
            int p;
            scanf("%d",&p);
            if(p)key[i]+=(1<<j);//鑰匙的可能性用狀壓來表示
        }
    }
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        int k1=0;
        for(int j=0;j<K;j++){
            int p;
            scanf("%d",&p);
            if(p)k1+=(1<<j);
        }
        edge[x].push_back((node){y,k1});
    }
    int ans=f();
    if(ans==-1)printf("No Solution\n");else printf("%d\n",ans);
    return 0;
}

三、小X的佛光
資料:
這裡寫圖片描述
計蒜客原題

既然最後一題都有表了,那就一分一分寫過來。

資料小一點的可以直接兩條路徑直接算是哪幾個點,然後輸出。這樣可以對前7個。
然後是A=C時,路徑長就是,那麼用倍增LCA一下就求出來了。
然後是鏈的情況。既然是一條鏈,那就分類討論一下,也很快。
當然鏈可以不用討論,公式是(abs(A-B)+abs(B-C)-abs(A-C))/2+1。什麼意思呢?第一個是A到B的距離,第二個是B到C的距離,第三個是A到C的距離。那麼前兩個相加減去後一個,那就是求相同的重複路徑。除以2是因為重複走,加1是因為求的是相同的點,而不是邊。
那麼在樹上就是一樣的了。

Code:

#include<bits/stdc++.h>
using namespace std;
vector<int>edge[200005];
int n,q,cas,dep[200005],fa[19][200005];
void f(int x,int fa1){//建樹
    dep[x]=dep[fa1]+1;fa[0][x]=fa1;
    for(int i=0;i<(int)edge[x].size();i++){
        int y=edge[x][i];
        if(y==fa1)continue;
        f(y,x);
    }
}
void Init(){//倍增初始化
    for(int j=1;j<19;j++)
        for(int i=1;i<=n;i++)
            fa[j][i]=fa[j-1][fa[j-1][i]];
}
int LCA(int x,int y){//求LCA
    if(dep[x]>dep[y])swap(x,y);
    int step=dep[y]-dep[x];
    for(int i=0;i<19;i++)
        if(step&(1<<i))y=fa[i][y];
    if(x==y)return x;
    for(int i=18;i>=0;i--)
        if(fa[i][x]!=fa[i][y])
            x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}
int main(){
    scanf("%d%d%d",&n,&q,&cas);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    f(1,0);
    Init();
    while(q--){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        int lca1=LCA(a,b),lca2=LCA(b,c),lca3=LCA(a,c);//分別是A-B,B-C,A-C的路徑上的LCA
        int L1=dep[a]+dep[b]-dep[lca1]*2,L2=dep[b]+dep[c]-2*dep[lca2],L3=dep[a]+dep[c]-2*dep[lca3];//路徑長度
        printf("%d\n",(L1+L2-L3)/2+1);//套一下公式
    }
    return 0;
}

又一次離AK這麼近,第二題的暴力坑死我了……(錯的其實是沒有輸出特殊的NO S什麼的)