NOI2015Day1 題解

Problem A 程序自動分析

傳送門

看到變量之間的相等關係具有傳遞性,所以不難想到思路:遇到xi=xjx_i=x_j的約束時用並查集把xix_ixjx_j代表的元素所在集合合併,遇到xi̸=xjx_i\not=x_j的約束時如果xix_ixjx_j代表的元素在同一個集合則矛盾。注意兩點:

  • 先處理相等關係,再處理不等關係;
  • 由於下標範圍很大,所以要離散化。

什麼?T10,N106T\le 10,N\le 10^6?不用擔心,經筆者實測,快讀+壓縮路徑並查集可以AC。

#include<bits/stdc++.h>
using namespace std;
const int N=1000005,N2=N<<1;
int rd(){
	int a=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))a=(a<<1)+(a<<3)+(ch^48),ch=getchar();
	return a;
}
int n,tot,f[N2],lis[N2],t;
struct node{
	int l,r,op;
}nod[N];
bool cmp(node a,node b){return b.op<a.op;}
int find(int a){if(f[a])return f[a]=find(f[a]);return a;}
int main(){
	t=rd();
	while(t--){
		n=rd(),tot=0;
		for(int i=1;i<=n;i++)nod[i]=(node){rd(),rd(),rd()},lis[++tot]=nod[i].l,lis[++tot]=nod[i].r;
		sort(nod+1,nod+n+1,cmp),sort(lis+1,lis+tot+1),tot=unique(lis+1,lis+tot+1)-lis-1;
		for(int i=1;i<=n;i++)nod[i]=(node){lower_bound(lis+1,lis+tot+1,nod[i].l)-lis,lower_bound(lis+1,lis+tot+1,nod[i].r)-lis,nod[i].op};
		memset(f,0,sizeof(f));int flag=1;
		for(int i=1;i<=n;i++){
			if(nod[i].op){
				int v=find(nod[i].l),u=find(nod[i].r);
				if(v!=u)f[v]=u;
			}
			else if(find(nod[i].l)==find(nod[i].r)){puts("NO"),flag=0;break;}
		}
		if(flag)puts("YES");
	}
	return 0;
}

Problem B 軟件包管理器

傳送門

如果把軟件之間的依賴關係抽象成樹的關係,那麼不難發現:

  • 安裝一個軟件時,要安裝從它到根節點路徑上的所有軟件;
  • 卸載一個軟件時,要卸載以它爲根節點的子樹中的所有軟件。

用樹剖+線段樹即可AC。真是一道樹剖入門好題啊

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
const int N=100005,N2=N<<2;
int n,q,s[N],sz[N],dfn[N],cnt,f[N],tp[N],last[N],tre[N2],lazy[N2],flag[N2];
vector<int>e[N];
void dfs1(int v){
	sz[v]=1;
	for(int i=0;i<(int)e[v].size();i++){
		int u=e[v][i];
		dfs1(u),sz[v]+=sz[u];
	}
}
void dfs2(int v,int top){
	dfn[v]=++cnt,tp[v]=top;
	int pre=0;
	for(int i=0;i<(int)e[v].size();i++){
		int u=e[v][i];
		if(sz[pre]<sz[u])pre=u;
	}
	if(pre)dfs2(pre,top);
	for(int i=0;i<(int)e[v].size();i++){
		int u=e[v][i];
		if(u!=pre)dfs2(u,u);
	}
	last[v]=cnt;
}
void pu(int c){tre[c]=tre[c<<1]+tre[c<<1|1];}
void paint(int l,int r,int v,int c){tre[c]=(r-l+1)*v,flag[c]=1,lazy[c]=v;}
void pd(int l,int r,int c){if(flag[c])paint(l,mid,lazy[c],c<<1),paint(mid+1,r,lazy[c],c<<1|1),flag[c]=0;}
void add(int l,int r,int L,int R,int v,int c){
	if(r<L||R<l)return;
	if(L<=l&&r<=R){paint(l,r,v,c);return;}
	pd(l,r,c);
	add(l,mid,L,R,v,c<<1),add(mid+1,r,L,R,v,c<<1|1);
	pu(c);
}
int main(){
	scanf("%d",&n);
	for(int i=2;i<=n;i++)scanf("%d",&f[i]),e[++f[i]].push_back(i);
	dfs1(1),dfs2(1,1);
	scanf("%d",&q);
	while(q--){
		char str[20];int lust=tre[1],a;
		scanf("%s%d",str,&a),++a;
		if(str[0]=='u')add(1,n,dfn[a],last[a],0,1);
		else{while(a)add(1,n,dfn[tp[a]],dfn[a],1,1),a=f[tp[a]];}
		printf("%d\n",abs(tre[1]-lust));
	}
	return 0;
}

Problem C 壽司晚宴

傳送門

這題是這套題最難的題,也是我做的時候唯一沒AC的題。

30分解法

首先要明白兩個數互質的含義:兩個數互質,當且僅當它們沒有共同的質因子。

所以可以求出每個數有哪些質因子,然後用狀壓dp(aia_i表示ii質因子的不重複集合):

  • f[i][S1ai][S2]+=f[i1][S1][S2]f[i][S_1\mid a_i][S_2]+=f[i-1][S_1][S_2],其中S2ai=S_2\bigcap a_i=\emptyset
  • f[i][S1][S2ai]+=f[i1][S1][S2]f[i][S_1][S_2\mid a_i]+=f[i-1][S_1][S_2],其中S1ai=S_1\bigcap a_i=\emptyset

其中第一維可以壓掉。

100分解法

上面的解法中當nn很大時可能的質因子也很多,所以肯定會TLE&MLE。那麼可不可以優化呢?答案是可以的。注意一個數nn最多有一個超過n\sqrt n的質因子,所以可以在狀壓dp方程中的集合中只考慮50022.3\le \sqrt{500}\approx22.3的質因子。剩下的質因子如何計算呢?

注意到一個質因子至多分給一個人,所以大質因子相同的數我們分成一塊:設f[i][S1][S2]f[i][S_1][S_2]是這一塊之前的方案數,g[i][0/1][S1][S2]g[i][0/1][S_1][S_2]爲當前只分給第一/二個人或者不分的方案數,則轉移方程如下:

  • g[i][0][S1ai][S2]+=g[i1][0][S1][S2]g[i][0][S_1\mid a_i][S_2]+=g[i-1][0][S_1][S_2],其中S2ai=S_2\bigcap a_i=\emptyset
  • g[i][1][S1][S2ai]+=g[i1][1][S1][S2]g[i][1][S_1][S_2\mid a_i]+=g[i-1][1][S_1][S_2],其中S1ai=S_1\bigcap a_i=\emptyset

特別的,不帶大質因子的每個數單獨分成一塊。

如何處理ff數組?

  • 一塊開始時把f[i][S1][S2]f[i][S_1][S_2]拷貝到g[i][0][S1][S2]g[i][0][S_1][S_2]g[i][1][S1][S2]g[i][1][S_1][S_2]
  • 一塊結束時更新f[i][S1][S2]=g[i][0][S1][S2]+g[i][1][S1][S2]f[i][S1][S2]f[i][S_1][S_2]=g[i][0][S_1][S_2]+g[i][1][S_1][S_2]-f[i][S_1][S_2]要去掉誰都不給的情況

其中第一維仍然可以壓掉。這樣我們就解決了本題!

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=505,prime[8]={2,3,5,7,11,13,17,19},n2=256;
int n;
ll p,f[N][N],g[2][N][N],ans;
struct node{
	int p,q;
}nd[N];
bool cmp(node a,node b){if(a.p==b.p)return a.q<b.q;return a.p<b.p;}
int main(){
	scanf("%d%lld",&n,&p);
	for(int i=2;i<=n;i++){
		int i1=i;
		for(int j=0;j<8;j++)if(!(i1%prime[j])){
			nd[i].q|=1<<j;
			while(!(i1%prime[j]))i1/=prime[j];
		}
		nd[i].p=i1;
	}
	sort(nd+2,nd+n+1,cmp);
	f[0][0]=1;
	for(int i=2;i<=n;i++){
		if(nd[i].p==1||nd[i].p!=nd[i-1].p)for(int j=0;j<n2;j++)for(int k=0;k<n2;k++)g[0][j][k]=g[1][j][k]=f[j][k];
		for(int j=n2-1;~j;j--)for(int k=n2-1;~k;k--){
			if(!(k&nd[i].q))(g[0][j|nd[i].q][k]+=g[0][j][k])%=p;
			if(!(j&nd[i].q))(g[1][j][k|nd[i].q]+=g[1][j][k])%=p;
		}
		if(nd[i].p==1||nd[i].p!=nd[i+1].p)for(int j=0;j<n2;j++)for(int k=0;k<n2;k++)f[j][k]=(g[0][j][k]+g[1][j][k]-f[j][k]+p)%p;
	}
	for(int i=0;i<n2;i++)for(int j=0;j<n2;j++)if(!(i&j))(ans+=f[i][j])%=p;
	printf("%lld",ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章