1. 程式人生 > >noip 2018 模擬賽1

noip 2018 模擬賽1

T1T_1——matrix(3118)

Description:

在一個nmn*m的只包含0,1的矩形中,求有多少個特殊的子矩形,特殊的字矩形滿足 它的四條邊上都是1,內部的0,1個數差不超過1,大小至少為2*2。 n,m300n,m\le300

Solution:

  • 對於找一個字矩形的問題,我們可以先嚐試地列舉一條邊,作為長,再列舉一下寬。這樣就是Θ(n2m)\Theta(n^2m)
  • 那麼剩下的判定一定是Θ(1)\Theta(1)的,即用字首和維護該邊1的個數。
  • 但是關鍵的條件是內部的0,1個數差不超過1,其實也就是在列舉寬的同時,維護一個0,1個數差的字首,接著記錄每個的0,1個數差的出現次數。
  • 那麼統計答案就可以由差為-1,0,1的狀態轉移即可。

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define ll long long

const int N=302,M=90000;

int n,m;
int A[N][N];
int col[N][N];
int cnt[M*2+5],mark[M*2+5];

int main(){
//	freopen("matrix.in","r",stdin);
//	freopen("matrix.out","w",stdout);
	scanf("%d%d",&n,&m);
	REP(i,1,n) REP(j,1,m) scanf("%d",&A[i][j]),col[i][j]=col[i-1][j]+A[i][j];
	
	int ans=0;
	REP(i,1,n) REP(j,i+1,n){
		int tot=0,sum=0,len=j-i+1;
		REP(k,1,m){
			int res=(len-2)-2*(col[j-1][k]-col[i][k]);
			if(len==col[j][k]-col[i-1][k]){
				ans+=cnt[sum+M]+cnt[sum+M-1]+cnt[sum+M+1];
                cnt[sum+M+res]++;
                mark[++tot]=sum+M+res;
			}
			if(!A[j][k] || !A[i][k]) {
				REP(l,1,tot)cnt[mark[l]]=0;
				tot=0;
			}
			sum+=res;
		}
		REP(l,1,tot) cnt[mark[l]]=0;
	} 
	printf("%d\n",ans);
	return 0;
}

T2T_2——build(2960)

Description:

一個有nn個格子的帶子,每個格子可以塗成黑色或白色,而對於一條塗好的帶子,對於每一個格子,它的花費為與之不同顏色的最近的距離和當前格子的顏色權值的乘積,即costi=disival[i][coli],colicost_i=dis_i*val[i][col_i],col_i為0或1,表示黑白。 求這帶子的最小花費。 n4000,val[i][0/1]105n\le4000,val[i][0/1]\le10^5

Solution:

  • 此題關鍵在於對於每個格子的與它不同顏色的格子的最近距離很難確定,或者說找到最優的。
  • 那麼我們就定義一個準確的方向,使得一些格子的最近距離一定在它的左邊或右邊,即分治。
  • 我們可以劃分兩個區間[L,mid],[mid+1,R][L,mid],[mid+1,R],使區間[L,mid][L,mid]的格子的最近距離都在它的右邊,區間[mid+1,R][mid+1,R]的格子的最近距離都在它的左邊。
  • 同樣是通過dpdp來定義狀態來找到最後的最優解。
  • dp[i][0/1]dp[i][0/1]表示前ii個格子,到第ii個格子是,它的狀態為黑色或白色的最優解。
  • 那麼轉移就是通過剛剛的分治思想,我們就需要預處理出對於每格子向左或向右走kk步的花費。

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}

const int N=4010;

int n;
struct node{
	int a,b;
}A[N];

struct p20{
	void solve(){
		SREP(i,0,n) scanf("%d%d",&A[i].a,&A[i].b);
		ll ans=0x3f3f3f3f;
		int t[11];
		SREP(s,1,(1<<n)-1){
			memset(t,0,sizeof(t));
			SREP(i,0,n) if(s&(1<<i)) t[i]=1;
			ll res=0;
			
			SREP(i,0,n){
				if(t[i]){
					REP(k,1,n) {
						if(i-k>=0 && !t[i-k]) {res+=k*A[i].b;break;}
						if(i+k<n && !t[i+k]) {res+=k*A[i].b;break;}
					}
				}
				else {
					REP(k,1,n){
						if(i-k>=0 && t[i-k]) {res+=k*A[i].a;break;}
						if(i+k<n && t[i+k]) {res+=k*A[i].a;break;}	
					}
				}
			}
			chkmin(ans,res);
		}
		printf("%lld\n",ans);
	}
}p1;

struct p100{
	
	int A[2][N];
	ll L[2][N],R[2][N],sum[2][N];
	ll dp[2][N];
	
	void solve(){
		memset(dp,-1,sizeof(dp));
		REP(i,1,n) scanf("%d%d",&A[0][i],&A[1][i]);
		SREP(i,0,2){
			REP(j,1,n) sum[i][j]=sum[i][j-1]+A[i][j];
			REP(j,1,n) L[i][j]=L[i][j-1]+j*A[i][j];
			DREP(j,n,1) R[i][j]=R[i][j+1]+(n+1-j)*A[i][j];
		}
		dp[0][0]=dp[1][0]=0;
		REP(i,1,n) SREP(j,0,i) SREP(k,0,2){
			ll res=0;
			int l=j+1,r=i;
			int mid=(j+i+1)>>1;
			if(j+1==1 && i==n) res=1e17;
			else if(j+1==1) res=R[!k][l]-R[!k][r+1]-(sum[!k][r]-sum[!k][l-1])*(n-r);
			else if(r==n) res=L[!k][r]-L[!k][l-1]-(sum[!k][r]-sum[!k][l-1])*(l-1);
			else res=L[!k][mid]-L[!k][l-1]-(sum[!k][mid]-sum[!k][l-1])*(l-1)+R[!k][mid+1]-R[!k][r+1]-(sum[!k][r]-sum[!k][mid])*(n-r);
			if(~dp[k][i]) chkmin(dp[k][i],dp[!k][j]+res);
			else dp[k][i]=dp[!k][j]+res;
		}
		printf("%lld\n",min(dp[0][n],dp[1][n]));
	}
}p2;

int main(){
//	freopen("build.in","r",stdin);
//	freopen("build.out","w",stdout);
	scanf("%d",&n);
	
	if(n<=10) p1.solve();
	else p2.solve();
	
	return 0;
}

T3T_3——network(3111)

Description:

有一棵nn個節點的樹,每個節點有一個valival_i,求出所有valval互質的節點的距離和。 n,vali105n,val_i\le10^5

Solution:

  • 一道非常傳統的資料結構題,首先對於互質,無非是要通過容斥或莫比烏斯反演來統計答案,而關鍵就是怎樣知道之間距離,發現複雜度是不夠的。
  • 這裡引入一個新知識——虛樹。
  • 虛樹的作用即保留原樹上有用的節點,期望虛樹的大小為logn\log n,這樣就能達到一個log\log,而接下來就是暴力莫比烏斯反演,而10510^5的質因數只有8個,因子只有128個,那麼複雜度為Θ(nlogn64)\Theta(n\log n*64)

Code:

#include<bits/stdc++.h>
using namespace std;
#define REP(i,f,t) for(int i=(f),i##_end_=(t);i<=i##_end_;++i)
#define SREP(i,f,t) for(int i=(f),i##_end_=(t);i<i##_end_;++i)
#define DREP(i,f,t) for(int i=(f),i##_end_=(t);i>=i##_end_;--i)
#define ll long long
template<class T>inline bool chkmin(T &x,T y){return x>y?x=y,1:0;}
template<class T>inline bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<class T>inline void Rd(T &x){
    x=0;char c;
    while((c=getchar())<48);
    do x=(x<<1)+(x<<3)+(c^48);
    while((c=getchar())>47);
}
 
const int N=1e5+2;
 
int n;
int val[N];
 
int head[N],qwq;
struct edge{
    int to,nxt;
}E[N<<1];
void addedge(int x,int y){E[qwq]=(edge){y,head[x]};head[x]=qwq++;}
 
int Mx;
 
int fa[N],dep[N],sz[N],son[N],top[N];

struct p30{
	
	int gcd(int a,int b){return !b?a:gcd(b,a%b);}
     
    void dfs1(int x,int f){
        fa[x]=f;
        dep[x]=dep[f]+1;
        sz[x]=1;
        for(int i=head[x];~i;i=E[i].nxt){
            int y=E[i].to;
            if(y==f)continue;
            dfs1(y,x);
            sz[x]+=sz[y];
            if(sz[son[x]]<sz[y])son[x]=y;
        }
    }
     
    void dfs2(int x,int tp){
        top[x]=tp;
        if(son[x]) dfs2(son[x],tp);
        for(int i=head[x];~i;i=E[i].nxt){
            int y=E[i].to;
            if(y==fa[x] || y==son[x]) continue;
            dfs2(y,y);
        }
    }
     
    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]]; 
        }
        return dep[x]<dep[y]?x:y;
    }
 
    void solve(){
        dfs1(1,0);
        dfs2(1,1);
 
        ll ans=0;
        REP(i,1,n) REP(j,i+1,n) if(gcd(val[i],val[j])==1){
            int lca=Lca(i,j);
            ans+=dep[i]+dep[j]-2*dep[lca]; 
        }
        printf("%lld\n",ans);
    }
}p1;
 
struct p60{
    #define NN 10002
    int c[NN][505],cnt1[505],cnt2[505];
    ll dp[NN][505],ans;
    vector<int>prime[505];
     
    void Init(){
        REP(i,2,500){
            int x=i;
            for(int j=2;j<=x;++j){
                if(x%j==0){
                    prime[i].push_back(j);
                    while(x%j==0)x/=j;
                }
            }
        }
    }
     
    void calc(int x){
        memset(cnt1,0,sizeof(cnt1));
        memset(cnt2,0,sizeof(cnt2));
        REP(i,1,Mx) for(int j=1;j*j<=i;++j) {
            if(i%j)continue;
            cnt1[j]+=dp[x][i];
            cnt2[j]+=c[x][i];
            if(i/j!=j) {
                cnt1[i/j]+=dp[x][i];
                cnt2[i/j]+=c[x][i];
            }
        }
    }
     
    void dfs(int x,int f){
        for(int i=head[x];~i;i=E[i].nxt){
            int y=E[i].to;
            if(y==f)continue;
            dfs(y,x);
             
            ll sum1=0,sum2=0;
            REP(j,1,Mx) sum1+=dp[x][j],sum2+=c[x][j];
            calc(x);
              
            REP(j,1,Mx){
                if(!c[y][j])continue;
                int l=(1<<(prime[j].size()));
                ll tot1=0,tot2=0;
                SREP(k,1,l){
                    int tmp=1,t1=0;
                    SREP(p,0,prime[j].size()) if(k&(1<<p))++t1,tmp*=prime[j][p];
                    if(t1&1)tot1+=cnt1[tmp],tot2+=cnt2[tmp];
                    else tot1-=cnt1[tmp],tot2-=cnt2[tmp];
                }
                ans+=(sum1-tot1)*c[y][j]+(sum2-tot2)*(dp[y][j]+c[y][j]);
            }
            REP(j,1,Mx){
                dp[x][j]+=dp[y][j]+c[y][j];
                c[x][j]+=c[y][j];
            }
        }
        calc(x);
        ll sum=0,tot=0;
        REP(j,1,Mx)sum+=dp[x][j];
        int l=(1<<(prime[val[x]].size()));
        SREP(i,1,l){
            int tmp=1,t1=0;
            SREP(j,0,prime[val[x]].size()) if(i&(1<<j))++t1,tmp*=prime[val[x]][j];
            if(t1&1)tot+=cnt1[tmp];
            else tot-=cnt1[tmp];
        }
        ans+=(sum-tot);
        c[x][val[x]]++;
    }
     
    void solve(){
        Init();
        dfs(1,0);
        printf("%lld\n",ans);
    }
}p2;

int dfn[N],lim[N],tim;
bool cmp(int x,int y){return dfn[x]<dfn[y];} 
struct p100{
	
	void dfs1(int x,int f){
		dfn[x]=tim++;
		fa[x]=f;
		dep[x]=dep[f]+1;
		sz[x]=1;
		son[x]=0;
		for(int i=head[x];~i;i=E[i].nxt){
			int y=E[i].to;
			if(y==f)continue;
			dfs1(y,x);
			sz[x]+=sz[y];
			if(sz[son[x]]<sz[y]) son[x]=y;
		}
		lim[x]=tim;
	}
	
	void dfs2(int x,int tp){
		top[x]=tp;
		if(son[x]) dfs2(son[x],tp);
		for(int i=head[x];~i;i=E[i].nxt){
			int y=E[i].to;
			if(y==fa[x] || y==son[x])continue;
			dfs2(y,y);
		}
	}
	
	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]]; 
	    }
	    return dep[x]<dep[y]?x:y;
	}
	
	int A[N],B[N],stk[N];
	int Sz[N];
	vector<int>T[N]; 
	int mu[N];
	
	ll calc(int k){
		int len=0,top=0;
	    for(int i=k;i<N;i+=k) SREP(j,0,T[i].size()) A[len++]=T[i][j];
		sort(A,A+len,cmp);
	    SREP(i,1,len) A[len++]=Lca(A[i-1],A[i]);
	    sort(A,A+len,cmp);
	    len=unique(A,A+len)-A;
	    
	    SREP(i,0,len){
	        int x=A[i];
	        while(top and lim[stk[top-1]]<=dfn[x]) --top;
	        B[x]=stk[top-1];
	        stk[top++]=x;
	    }
	    
	    ll res=0;
	    int tot=0;
	    SREP(i,0,len){
	        Sz[A[i]]=(val[A[i]]%k==0);
	        tot+=Sz[A[i]];
	    }
	    DREP(i,len-1,1){
	        int x=A[i],y=B[x];
	        res+=1ll*(dep[x]-dep[y])*Sz[x]*(tot-Sz[x]);
	        Sz[y]+=Sz[x];
	    }
	    return res;
	}
	
	void solve(){
		dfs1(1,0),dfs2(1,1);
		REP(i,1,n) T[val[i]].push_back(i);
		ll ans=0;
		mu[1]=1;
		SREP(i,1,N) if(mu[i]) {
			for(int j=i+i;j<N;j+=i) mu[j]-=mu[i];
			ans+=calc(i)*mu[i];
		}
		printf("%lld\n",ans);
	}
	
}p3;
 
int main(){
//  	freopen("network.in","r",stdin);
//  	freopen("network.out","w",stdout);
    memset(head,-1,sizeof(head));
    Rd(n);
    REP(i,1,n) Rd(val[i]),chkmax(Mx,val[i]);
    SREP(i,1,n){
        int a,b;
        Rd(a),Rd(b);
        addedge(a,b);
        addedge(b,a);
    }
     
    if(n<=500) p1.solve();
    else if(Mx<=500) p2.solve();
    else p3.solve();
    
    return 0;
}

總結:

  • 感覺大部分時間還是在磨T2T_2,對於dpdp的轉移還是太想當然了,有時候還需要藉助其它思想或資料結構來維護,不僅僅是簡單的轉移。
  • 對於虛樹,感覺這又是一個暴力的資料結構,而關鍵建虛樹是要非常明確的知道哪些是有用的點。