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\n",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\n",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?‘\n‘:‘ ‘);
	}
};
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\n",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\n",f[s][0]);
	return 0;
}

  

时间: 2024-11-10 08:16:11

51nod算法马拉松13的相关文章

51Nod 算法马拉松21(迎新年)

这次打算法马拉松是在星期五的晚上,发挥还算正常(废话,剩下的题都不会= =). 讲讲比赛经过吧. 8:00准时发题,拿到之后第一时间开始读. A配对,看上去像是二分图最大权匹配,一看范围吓傻了,先跳过读后面的题. B完全二叉树的方差,大概看了一遍,好神的样子,跳过. C多项式?好吧没学过FFT和NTT的我肯定不会,跳跳跳. D最大值,哎呦这函数什么破玩意儿,看不懂,跳跳跳. E B君的射击,卧槽毕克大人您出题就算了出这么一道码农题是要闹那样,跳跳跳. F那些年,我们一起讲的故事,卧槽这特么简直就

随便玩玩系列之一:SPOJ-RNG+51nod 算法马拉松17F+51nod 1034 骨牌覆盖v3

先说说前面的SPOJ-RNG吧,题意就是给n个数,x1,x2,...,xn 每次可以生成[-x1,x1]范围的浮点数,把n次这种操作生成的数之和加起来,为s,求s在[A,B]内的概率 连续形的概率,想象为一个n维长方体,有两个平面与这个几何图形相割,于是就变成了求面(体)积问题,一般要去重,n维区域系数:s^n/n!,至于区间问题,直接前缀之差搞定 然后就是悲催的算法马拉松17F题了...其实是道好题来的,只是出题人不知世界上还有这题,然后某大牛把思路理清后把答案直接搬了过来 经典的1*2骨牌覆

51Nod 算法马拉松15 记一次悲壮而又开心的骗分比赛

OwO 故事的起源大概是zcg前天发现51Nod晚上有场马拉松,然后他就很开心的过去打了 神奇的故事就开始了: 晚上的时候我当时貌似正在写线段树?然后看见zcg一脸激动告诉我第一题有九个点直接输出B就可以A.. 然后之后zcg以奇怪的二分方式发现了如何A掉第一题的第十个点(我记得貌似是什么第5000个数等于511? OwO 就这样没有任何思考含量全凭骗分黑科技过掉了第一题 OwO 然后zcg打开了第二题,发现第二题样例有点问题,然后就发了个帖子,直接去看第三题了 我去瞅了一眼,发现这不是gcd

51nod算法马拉松 contest7

A题 链接:http://www.51nod.com/contest/problem.html#!problemId=1417 推荐链接:http://blog.csdn.net/a837199685/article/details/45009337 设美女取得正面概率是p,反面就是(1-p),就是美女取一正一反和一反一正的概率相同,然后推出公式y=((a+b)/2+b)/(2*(a+b)); 1 #include<iostream> 2 #include<cstdio> 3 #i

51Nod 算法马拉松23 开黑记

惨啊--虽然开了半天黑,但是还是被dalao们踩了-- 第二次开黑,还是被卡在rank20了,我好菜啊--= = 写一写比赛经过吧-- 看到题之后习惯性都打开,A~D看上去似乎并没有什么思路,F应该是道数论题,看了E感觉有点意思,一看数据范围,咦怎么只有$50000$,再仔细看一看式子,手动分情况讨论之后得到一个结论-- 这题是水的线段树维护莫队啊= = 然后就开始码码码,由于一些脑残错误调了一会儿,然后就得到了这样的结果: 我坚信自己莫队的复杂度没错,然后就开始各种王逸松卡常,使用的卡常技巧包

51nod 算法马拉松4

http://www.51nod.com/contest/problemList.html#!contestId=9 D 装盒子 拓扑排序?

51NOD算法马拉松 最大值问题 离线预处理+set lower_bound

题目:http://www.51nod.com/contest/problem.html#!problemId=1349 题意:100000个数的序列,有100000次询问,每次问区间最大值大于等于k的区间有多少? 思路:一开始没看到"大于等于",想了很久也不会,原来看错题了.看错题害死人. 一般询问的问题,如果不能用线段树log(n)求出,那么就离线做. 首先将询问按从大到小排序,再将序列中的每个数排序,注意记录序号. 对于当前询问,每加进一个数,我需要找到它在加进的序列(按大小有序

51nod 算法马拉松4 D装盒子(网络流 / 二分图最优匹配)

装盒子 基准时间限制:1 秒 空间限制:131072 KB 分值: 160 有n个长方形盒子,第i个长度为Li,宽度为Wi,我们需要把他们套放.注意一个盒子只可以套入长和宽分别不小于它的盒子,并且一个盒子里最多只能直接装入另外一个盒子 (但是可以不断嵌套),例如1 * 1 可以套入2 * 1,而2 * 1再套入2 * 2.套入之后盒子占地面积是最外面盒子的占地面积.给定N个盒子大小,求最终最小的总占地面积. Input 第一行一个数N表示盒子的个数. 接下来N行,每行两个正整数,表示每个盒子的长

51nod 算法马拉松4 B递归(YY)

递归 基准时间限制:1 秒 空间限制:131072 KB 分值: 80 函数f(n,m) { 若n=1或m=1返回a[n][m]; 返回f(n-1,m)异或f(n,m-1); } 读入2<=n,m<=100 for i=2->100读入a[1][i] for i=2->100读入a[i][1] 输出f(n,m) 发现当n,m较大时程序变得异常缓慢. 小b经过一番思考,很快解决了这个问题. 这时小c出现了,我将n,m都增加131072,你还能解决吗? 相对的,我会读入2->13