9.11第一轮扫荡战果

1.处理内容

图论部

         hall定理 1题

         最小生成树 1题

数学几何部

         博弈论 1题

         小学找规律 1题

         矩阵树定理 1题

动态规划部

         简单线性DP 1题

         非经典DP 1题

         斜率优化DP 1题

2.图论部

(1)guard(JZOJ)

没有链接,略

hall定理链接:https://baike.baidu.com/item/Hall%E5%AE%9A%E7%90%86/5111749?fr=aladdin

虽然看上去是废话...

(2)tree(BZOJ2356)

题面见链接http://www.lydsy.com/JudgeOnline/problem.php?id=2654

既然限制了白边数量,那么二分一下对白边的加权即可

黄松皓先生曾经在我校讲过这道题

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
#define stan 55555
#define sten 111111
struct edge{
	int a,b,c,d;
}e[sten];
int n,m,k,fa[stan],a[sten],b[sten],c[sten],d[sten],tag,mini,mid,ans;
bool cmp(edge x,edge y){
	if(x.d!=y.d) return x.dy.c;
}
int getfa(int x){
	if(fa[x]!=x) return fa[x]=getfa(fa[x]);
	else return x;
}
bool check(int data){
	mini=0;
	int cnt=0,tot=0;
	for(int i=1;i<=n;++i) fa[i]=i;
	for(int i=1;i<=m;++i){
		e[i].a=a[i];
		e[i].b=b[i];
		e[i].c=c[i];
		e[i].d=d[i]+c[i]*mid;
	}
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;++i){
		int x=getfa(e[i].a);
		int y=getfa(e[i].b);
		if(x!=y){
			fa[x]=y;
			mini+=e[i].d;
			if(e[i].c) ++cnt;
			++tot;
		}
		if(tot==n-1) break;
	}
	return cnt>=k;
}
signed main(){
	n=read();m=read();k=read();
	for(int i=1;i<=m;++i){
		a[i]=read()+1;
		b[i]=read()+1;
		d[i]=read();
		c[i]=read()^1;
	}
	int l=-100,r=100;
	while(l<=r){
		mid=l+r>>1;
		if(check(mid)){
			ans=mini-mid*k;
			l=mid+1;
		}else
			r=mid-1;
	}
	write(ans);
	return 0;
}
3.数学几何部

(1)Nim(POJ2068)

题面见链接http://poj.org/problem?id=2068

貌似可以直接搜:下家的必输态是当前的必胜态

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
int n,s,f[22][8888],val[22];
int solve(int pos,int remain){
	if(pos==n*2+1) pos=1;
	if(f[pos][remain]!=-1)
		return f[pos][remain];
	if(remain==0)
		return f[pos][remain]=1;
	f[pos][remain]=0;
	for(int i=1;i<=min(val[pos],remain);++i)
		if(!solve(pos+1,remain-i)) f[pos][remain]=1;
	return f[pos][remain];
}
signed main(){
	n=read();
	while(n){
		memset(f,-1,sizeof(f));
		s=read();
		for(int i=1;i<=n*2;++i)
			val[i]=read();
		write(solve(1,s));
		puts(" ");
		n=read();
	}return 0;}
(2)Function

对于一个整数,定义f(x)为其每个数位阶乘的乘积。

对于一个整数a,求最大整数x使f(x)==f(a)

貌似直接分解质因数即可

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
int n,cnt2,cnt3,cnt5,cnt7;
char num[22];
void split(char x){
	int m=x-48;
	for(int i=2;i<=m;++i){
		int tmp=i;
		while(tmp%2==0){
			++cnt2;
			tmp>>=1;
		}
		while(tmp%3==0){
			++cnt3;
			tmp/=3;
		}
		while(tmp%5==0){
			++cnt5;
			tmp/=5;
		}
		while(tmp%7==0){
			++cnt7;
			tmp/=7;
		}
	}
}
signed main(){
	n=read();
	scanf("%s",num+1);
	for(int i=1;i<=n;++i)
		split(num[i]);
	for(int i=1;i<=cnt7;++i)
		putchar('7');
	cnt2-=4*cnt7;cnt3-=2*cnt7;cnt5-=cnt7;
	for(int i=1;i<=cnt5;++i)
		putchar('5');
	cnt2-=3*cnt5;cnt3-=cnt5;
	for(int i=1;i<=cnt3;++i)
		putchar('3');
	cnt2-=cnt3;
	for(int i=1;i<=cnt2;++i)
		putchar('2');
	return 0;
}
(3)矩阵树定理

直接甩链接https://wenku.baidu.com/view/872eb02de2bd960590c677c6.html

只写了第一道例题

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define int long long
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
int T,c[22][22],tmp[22],n,m,a,b,ans;
signed main(){
	T=read();
	while(T--){
		ans=1;
		memset(c,0,sizeof(c));
		n=read();m=read();
		for(int i=1;i<=m;++i){
			a=read();b=read();
			--a;--b;
			++c[a][a];++c[b][b];
			c[a][b]=c[b][a]=-1;
		}
		for(int i=1;i
4.动态规划部

(1)牡牛和牝牛(USACO2009Feb)

http://hzwer.com/4574.html题面在左边

其实可以O(n)DP水过

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define int long long
#define mod 5000011
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
int n,k,f[111111];
signed main(){
	n=read();k=read()+1;
	f[1]=2;
	for(int i=2;i<=n;++i)
		if(i-k<=0) f[i]=(f[i-1]+1)%mod;
		else f[i]=(f[i-1]+f[i-k])%mod;
	write(f[n]);
	return 0;
}
(2)合并石子

链接如下

http://blog.csdn.net/acdreamers/article/details/18039073

http://blog.csdn.net/dad3zz/article/details/50784554

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
int n,a[55555],tmp,ans;
void merge(int k){
	int val=a[k]+a[k-1];
	ans+=val;
	for(int i=k;i1;--j) a[j]=a[j-1];
    a[j]=val;--tmp;
    for(int d=tmp-j;j>2&&a[j-2]<=a[j];d=tmp-j)
        merge(j-1),
        j=tmp-d;
}
signed main(){
	n=read();
	while(n){
		for(int i=1;i<=n;++i)
			a[i]=read();
		for(int i=1;i<=n;++i){
			a[++tmp]=a[i];
			while(tmp>2&&a[tmp-2]<=a[tmp]) merge(tmp-1);
		}
		for(;tmp>1;) merge(tmp);
		write(ans);putchar('\n');
		ans=0;
		n=read();
		tmp=0;
		memset(a,0,sizeof(a));
	}
	return 0;
}
(3)Cash(NOI2007)

题面太长决定直接甩链接http://www.lydsy.com/JudgeOnline/problem.php?id=1492

乍一看是可以斜率优化的

观察HINT可以猜到每天要么全买进,要么全卖出

我们规定f[i]为第i天的最大收益,trade[i].x表示i天买入的a劵数,trade[i].y表示第i天买入的b劵数,trade[i].a表示第i天单份a劵收益,trade[i].b表示第i天单份b劵收益

则第i天卖的收益可以表示为

max(trade[j].y*trade[i].b+trade[j].x*trade[j].a);

同时我们也可以得到trade[i].y=f[i]/(trade[i].a*trade[i].r+trade[i].b),trade[i].x=trade[i].y*trade[i].rate

如果我们把其当做有单调性的,我们会得到

(trade[k].y-trade[j].y)/(trade[k].x-trade[j].x)>-a[i]/b[i];

但很可惜的是-a[i]/b[i]并不具备单调性,因此我们被迫要维护一个动态凸包

很显然我们可以维护一棵平衡树

但是我们也可以CDQ

在这里推荐hzw大佬的代码及注释http://hzwer.com/3508.html

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define eps 1e-9
using namespace std;
inline int read(){
	int i=0,f=1;
	char ch;
	for(ch=getchar();!isdigit(ch);ch=getchar())
		if(ch=='-') f=-1;
	for(;isdigit(ch);ch=getchar())
		i=(i<<3)+(i<<1)+(ch^48);
	return i*f;
}
int buf[1024];
inline void write(int x){
	if(!x){putchar('0');return ;}
	if(x<0){putchar('-');x=-x;}
	while(x){buf[++buf[0]]=x%10,x/=10;}
	while(buf[0]) putchar(buf[buf[0]--]+48);
	return ;
}
#define stan 111111
struct commerce{
	double a,b,r,slope,x,y;
	int pos;
}trade[stan],tmp[stan];
int n,que[stan],top;
double f[stan];
inline bool cmp(commerce a,commerce b){
	return a.slope>b.slope;
}
double calc(int a,int b){
	if(!b) return -1e20;
	if(fabs(trade[a].x-trade[b].x)<=eps) return 1e20;
	else return (trade[a].y-trade[b].y)/(trade[a].x-trade[b].x);
}
void cdq(int l,int r){
	if(l==r){
		f[l]=max(f[l],f[l-1]);
		trade[l].y=f[l]/(trade[l].a*trade[l].r+trade[l].b);
		trade[l].x=trade[l].y*trade[l].r;
		return ;
	}
	int mid=l+r>>1;
	int pre=l,suf=mid+1;
	for(int i=l;i<=r;++i)
		if(trade[i].pos<=mid)
			tmp[pre++]=trade[i];
		else tmp[suf++]=trade[i];
	for(int i=l;i<=r;++i)
		trade[i]=tmp[i];
	cdq(l,mid);
	top=0;
	for(int i=l;i<=mid;++i){
		while(top>1&&calc(que[top-1],que[top])trade[i].slope) ++j;
		f[trade[i].pos]=max(f[trade[i].pos],trade[que[j]].x*trade[i].a+trade[que[j]].y*trade[i].b);
	}
	cdq(mid+1,r);
	pre=l;suf=mid+1;
	for(int i=l;i<=r;++i)
		if(((trade[pre].xr)&&pre<=mid)
			tmp[i]=trade[pre++];
		else tmp[i]=trade[suf++];
	for(int i=l;i<=r;++i)
		trade[i]=tmp[i];
	return ;
}
signed main(){
	n=read();f[0]=read();
	for(int i=1;i<=n;++i){
		scanf("%lf%lf%lf",&trade[i].a,&trade[i].b,&trade[i].r);
		trade[i].slope=-trade[i].a/trade[i].b;
		trade[i].pos=i;
	}
	sort(trade+1,trade+n+1,cmp);
	cdq(1,n);
	printf("%.3lf",f[n]);
	return 0;
}
我还是装模作样写一份代码比较好

局部完结撒花



你可能感兴趣的:(OI,最小生成树科,分治纲,琐题集萃门,动态规划纲,图论纲)