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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
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.d<y.d;
else return x.c>y.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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#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<n;++i){
for(int j=i+1;j<n;++j)
while(c[j][i]){
int tmp=c[i][i]/c[j][i];
for(int k=i;k<n;++k) c[i][k]-=c[j][k]*tmp;
for(int k=i;k<n;++k) swap(c[i][k],c[j][k]);
ans=-ans;
}
ans*=c[i][i];
if(!ans) break;
}
if(ans<0) ans=-ans;
write(ans);puts(" ");
}
return 0;
}
4.动态规划部
(1)牡牛和牝牛(USACO2009Feb)
http://hzwer.com/4574.html题面在左边
其实可以O(n)DP水过
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
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;i<tmp;++i)
a[i]=a[i+1];
int j;
for(j=k-1;a[j-1]<val&&j>1;--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<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#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])<calc(que[top-1],i)+eps) --top;
que[++top]=i;
}
que[++top]=0;int j=1;
for(int i=mid+1;i<=r;++i){
while(j<top&&calc(que[j],que[j+1])+eps>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].x<trade[suf].x||(fabs(trade[pre].x-trade[suf].x)<eps&&trade[pre].y<trade[suf].y))||suf>r)&&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;
}
我还是装模作样写一份代码比较好
局部完结撒花