10.15離線賽
阿新 • • 發佈:2019-02-11
一、小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什麼的)