zoukankan      html  css  js  c++  java
  • 51nod算法马拉松13

    A 取余最长路

    不难发现路径可以拆成三条线段,只要知道两个转折点的位置就能计算出答案。

    设sum(i,l,r)表示第i行从l到r元素的和,则答案可以表示为sum(1,1,x)+sum(2,x,y)+sum(3,y,n)%p。

    前缀和一下转化成(S3[n]-S3[y-1])+S2[y]+(S1[x]-S2[x-1])%p,从小到大枚举y,将所有(S1[x]-S2[x-1])扔到一个集合里,用个set就能轻松实现了。

    时间复杂度为O(NlogN)。

    #include<cstdio>
    #include<cctype>
    #include<set>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int maxn=100010;
    ll S1[maxn],S2[maxn],S3[maxn],ans;
    set<ll> S;
    set<ll>::iterator it;
    int main() {
    	int n=read(),p=read();
    	rep(i,1,n) S1[i]=(S1[i-1]+read())%p;
    	rep(i,1,n) S2[i]=(S2[i-1]+read())%p;
    	rep(i,1,n) S3[i]=read();
    	dwn(i,n,1) S3[i]+=S3[i+1];
    	rep(i,1,n) {
    		ll val;val=(S1[i]-S2[i-1]+p)%p;
    		S.insert(val);val=(S3[i]+S2[i])%p;
    		it=S.lower_bound(p-val);
    		if(it==S.begin()) ans=max(ans,(val+(*(--S.end())))%p);
    		else ans=max(ans,(val+(*(--it)))%p);
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    B 树有几多愁

    不难发现这样几个性质:

    1.节点的编号肯定是按深度递减的。(可以用相邻交换法证明)

    2.如果知道了叶节点大小的相对顺序就能唯一还原出整棵树的编号。(由性质1不难推出)

    那么我们设f[S]表示将S集合的叶节点都标上号后的答案,因为乘积会很大,所以我们取对数记录一下方案,最后再乘回去就行了。

    然后为了方便转移随便dfs算算每种状态标了多少号就行了,时间复杂度为O(2^c*c+c*n)。

    #include<cstdio>
    #include<cctype>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    typedef long long ll;
    const int maxn=100010;
    int n,first[maxn],in[maxn],next[maxn<<1],to[maxn<<1],e;
    void AddEdge(int u,int v) {
    	in[v]++;to[++e]=v;next[e]=first[u];first[u]=e;
    	in[u]++;to[++e]=u;next[e]=first[v];first[v]=e;
    }
    int val[maxn],A[maxn],fa[maxn],pa[maxn],m,dep[maxn];
    int S[maxn],vis[maxn],top;
    void dfs(int x) {
    	S[++top]=x;
    	while(top) {
    		x=S[top];
    		if(!vis[x]) {
    			int tp=pa[x];vis[x]=1;
    			dep[x]=dep[fa[x]]+1;if(!fa[x]||in[x]!=2) tp=x;
    			ren if(to[i]!=fa[x]) fa[to[i]]=x,pa[to[i]]=tp,S[++top]=to[i];
    		}
    		else {
    			ren if(to[i]!=fa[x]) val[x]|=val[to[i]];
    			if(in[x]==1&&fa[x]) A[m++]=x,val[x]=1<<m-1;
    			top--;
    		}
    	}	
    }
    int g[1<<20],p[1<<20];
    const int mod=1000000007;
    int calc(int x,int S) {
    	int res=dep[x];
    	while(x&&((S&val[x])==val[x])) x=pa[x];
    	return res-dep[x];
    }
    double f[1<<20];
    int num[maxn];
    void solve(int S) {
    	if(!S) return;
    	solve(S^(1<<p[S]));
    	num[A[p[S]]]=g[S^(1<<p[S])]+1;
    }
    int main() {
    	n=read();
    	rep(i,2,n) AddEdge(read(),read());
    	dfs(1);
    	rep(S,1,(1<<m)-1) {
    		rep(i,0,m-1) if(S>>i&1) {
    			g[S]=g[S^(1<<i)]+calc(A[i],S);
    			break;
    		}
    	}
    	rep(S,1,(1<<m)-1) {
    		f[S]=-1e50;
    		rep(i,0,m-1) if(S>>i&1) {
    			double res=f[S^(1<<i)]+log2(g[S^(1<<i)]+1);
    			if(res>f[S]) f[S]=res,p[S]=i;
    		}
    	}
    	solve((1<<m)-1);
    	ll ans=1;
    	rep(i,0,m-1) (ans*=num[A[i]])%=mod;
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    C 比大小

    首先解方程得到B[0]=-1,然后打一下表相信你就能发现规律(提示:按mod4分类)

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    typedef long long ll;
    inline ll read() {
        ll x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    struct Matrix {
    	int A[2][2];
    	Matrix operator * (Matrix& b) {
    		Matrix c;
    		rep(i,0,1) rep(j,0,1) {
    			c.A[i][j]=0;
    			rep(k,0,1) c.A[i][j]+=A[i][k]*b.A[k][j];
    			c.A[i][j]%=4;
    		}
    		return c;
    	}
    	void print() {
    		rep(i,0,1) rep(j,0,1) printf("%d%c",A[i][j],j==1?'
    ':' ');
    	}
    };
    void pow(Matrix& ans,ll n) {
    	Matrix A;A=ans;
    	ans.A[0][0]=1;ans.A[0][1]=0;
    	ans.A[1][0]=0;ans.A[1][1]=1;
    	while(n) {
    		if(n&1) ans=ans*A;
    		A=A*A;n>>=1;
    	}
    }
    void solve() {
    	ll A0=read(),a=read(),b=read(),n=read();
    	Matrix T,ans;
    	T.A[0][0]=a%4;T.A[0][1]=b%4;
    	T.A[1][0]=0;T.A[1][1]=1;
    	ans.A[0][0]=A0%4;ans.A[0][1]=0;
    	ans.A[1][0]=1;ans.A[1][1]=0;
    	pow(T,n);ans=T*ans;
    	if(ans.A[0][0]==1) puts("=");
    	else if(ans.A[0][0]%2==0) puts("<");
    	else puts(">");
    }
    int main() {
    	dwn(T,read(),1) solve();
    	return 0;
    }
    

    D 有限背包计数问题

    我们先考虑一种暴力的DP做法:设f[i][j]表示用前i个物品装满容量为j的背包的方案数,然后做个多重背包就行了,时间复杂度O(N^2)。

    我们再来考虑一种暴力的DP做法:设f[i][j]表示用i个物品(不考虑个数限制)装满容量为j的背包的方案数,考虑这i个物品中最小的物品,如果它是1,则f[i][j]+=f[i-1][j-1],否则说明这i个物品均大于1,f[i][j]+=f[i][j-i]。

    然后我们发现对于<=sqrt(N)的物品,用第一种做法就行了,对于>sqrt(N)的物品,肯定不会使用超过sqrt(N)个,而且每个物品肯定够用,用第二种做法就行了。

    最后滚动一下数组把答案合并起来就行了,时间复杂度为O(Nsqrt(N))。

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=100010;
    const int mod=23333333;
    typedef long long ll;
    int n;
    int f[2][maxn],g[2][maxn],g2[maxn],sum[maxn];
    int main() {
    	n=read();int SIZE=(int)sqrt(n);
    	if(n==1) puts("1");
    	else if(n==2) puts("1");
    	else if(n==3) puts("2");
    	else if(n==4) puts("3");
    	else {
    		int cur=0;f[0][0]=1;
    		rep(i,1,SIZE) {
    			cur^=1;
    			rep(j,0,i-1) {
    				for(int k=j;k<=n;k+=i) sum[k]=((k<i?0:sum[k-i])+f[cur^1][k])%mod;
    				for(int k=j;k<=n;k+=i) {
    					f[cur][k]=(sum[k]-(k<i*(i+1)?0:sum[k-i*(i+1)])+mod)%mod;
    				}
    			}
    		}
    		ll ans=0;int cr=0;
    		g[0][0]=g2[0]=1;
    		rep(i,1,SIZE) {
    			cr^=1;memset(g[cr],0,sizeof(g[cr]));
    			if(i>1) rep(j,SIZE+1,n) g[cr][j]=(g[cr][j-i]+g[cr^1][j-SIZE-1])%mod;
    			else rep(j,SIZE+1,n) g[cr][j]=1;
    			rep(j,SIZE+1,n) (g2[j]+=g[cr][j])%=mod;
    		}
    		rep(A,0,n) (ans+=(ll)g2[A]*f[cur][n-A])%=mod;
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    E B君的骗局

    我们可以设f[x][S]表示当前在x节点,已走过状态为S,期望再走几步才能结束整个过程。

    直接暴力消元肯定是不行的,但我们按S分一下层,做512次消元再压一下常数就行了。

    时间复杂度为O(2^c*N^3)。

    #include<cstdio>
    #include<cctype>
    #include<queue>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define dwn(i,s,t) for(int i=s;i>=t;i--)
    #define ren for(int i=first[x];i;i=next[i])
    using namespace std;
    const int BufferSize=1<<16;
    char buffer[BufferSize],*head,*tail;
    inline char Getchar() {
    	if(head==tail) {
    		int l=fread(buffer,1,BufferSize,stdin);
    		tail=(head=buffer)+l;
    	}
    	return *head++;
    }
    inline int read() {
        int x=0,f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    const int maxn=55;
    const double eps=1e-12;
    typedef double Matrix[maxn][maxn];
    const int maxm=550;
    int n,m,s,x[9],e[maxn][maxn];
    void gauss(Matrix& A) {
    	rep(i,0,n-1) {
    		int r=i;
    		rep(j,i+1,n-1) if(fabs(A[j][i])>fabs(A[r][i])) r=j;
    		if(fabs(A[r][i])<eps) continue;
    		if(r!=i) rep(j,0,n) swap(A[i][j],A[r][j]);
    		rep(k,0,n-1) if(k!=i)
    			dwn(j,n,i) A[k][j]-=A[k][i]/A[i][i]*A[i][j];
    	}
    }
    double f[maxn][maxm];
    int check(int S) {
    	int c1=0,c2=0,c3=0,c=0;
    	if(S&1) c1++;if(S&2) c1++;if(S&4) c1++;
    	if(S&8) c2++;if(S&16) c2++;if(S&32) c2++;
    	if(S&64) c3++;if(S&128) c3++;if(S&256) c3++;
    	if(c1>=2) c++;if(c2>=2) c++;if(c3>=2) c++;
    	return c>=2;
    }
    Matrix A;
    int main() {
    	n=read();m=read();
    	rep(i,1,m) {
    		int u=read(),v=read();
    		e[u][v]=e[v][u]=1;
    	}
    	rep(i,0,8) x[i]=read();s=read();
    	dwn(S,511,0) {
    		if(!check(S)) {
    			rep(i,0,n) rep(j,0,n) A[i][j]=0;
    			rep(i,0,n-1) {
    				int j=0;
    				rep(k,0,8) if(x[k]==i) j|=(1<<k);
    				if(j&&(!(S&j))) {A[i][i]=1;A[i][n]=f[i][S|j];continue;}
    				A[i][i]=1;A[i][n]=1;int cnt=0;
    				rep(v,0,n-1) if(e[i][v]) cnt++;
    				rep(v,0,n-1) if(e[i][v]) A[i][v]=-1.0/cnt;
    			}
    			gauss(A);
    			rep(i,0,n-1) if(fabs(A[i][i])>eps) f[i][S]=A[i][n]/A[i][i];
    		}
    	}
    	printf("%.6lf
    ",f[s][0]);
    	return 0;
    }
    

      

  • 相关阅读:
    【PHP】算法: 获取满足给定值的最优组合
    @程序员,你还记得当年高考时的样子吗?
    教妹学 Java:难以驾驭的多线程
    二十九岁,刚读完了财富启蒙读物《小狗钱钱》
    蓦然回首,Java 已经 24 岁了!
    @程序员,你需要点金融常识
    教妹学 Java:大有可为的集合
    @程序员,你需要点财商
    教妹学 Java:晦涩难懂的泛型
    大量阅读,并不等同于“走马观花”
  • 原文地址:https://www.cnblogs.com/wzj-is-a-juruo/p/5453140.html
Copyright © 2011-2022 走看看