NOI2015填坑
Day1
T1 签到题
T2 树剖裸题
T3 神题:
考场上面就没想出来
重新往状压DP方向想了一(hen)下(jiu)发现依旧没想出来
因为每个数大于sqrt(n)的质因数最多只有1个,所以可以状压前8个质数,然后把每个数依次加进来计算方案。
设f(S,T)为第一个人的状态为S且第二个人状态为T的时候的当前方案个数,然后还设个g0/g1(S,T)分别是两个人选择当前这个质因数的时候的方案数,最后处理完这个质因数就把f处理成g0+g1-f,因为两个都不选算了两遍。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 256
#define M 505
#define fr first
#define sc second
#define ll long long
pair <int,int> li[M];
int f[N][N],g[2][N][N],n,m,ans;
const int Prime[]={2,3,5,7,11,13,17,19};
inline int Mod(ll x)
{return (x+m)%m;}
int main()
{
cin >>n>>m;
for (int i=2;i<=500;i++)
{
int k=i;
for (int j=0;j<8&&k-1;j++)
while (k%Prime[j]==0)
k/=Prime[j],li[i-1].sc|=1 << j;
li[i-1].fr=k;
}
sort(li+1,li+n);
f[0][0]=1;
for (int i=1;i<n;i++)
{
if (li[i].fr==1||li[i].fr!=li[i-1].fr)
memcpy(g[0],f,sizeof f),memcpy(g[1],f,sizeof f);
for (int k=N-1;k>=0;k--)
for (int l=N-1;l>=0;l--)
if ((l&li[i].sc)==0)
g[0][k|li[i].sc][l]=Mod(g[0][k|li[i].sc][l]+g[0][k][l]);
for (int k=N-1;k>=0;k--)
if ((k&li[i].sc)==0)
for (int l=N-1;l>=0;l--)
g[1][k][l|li[i].sc]=Mod(g[1][k][l|li[i].sc]+g[1][k][l]);
if (li[i].fr!=li[i+1].fr||li[i].fr==1)
for (int k=0;k<N;k++)
for (int l=0;l<N;l++)
f[k][l]=Mod(g[0][k][l]+g[1][k][l]-f[k][l]);
}
for (int i=0;i<N;i++)
for (int j=0;j<N;j++)
ans=Mod(ans+f[i][j]);
cout <<ans<<endl;
return 0;
}
Day2
T1:按哈弗曼树那样贪心,只不过一次选取最小的k个,并且高度也要贪心选取。如果n%k!=0就补位。
由于是考场上面A的就懒得放代码了= =
T2:后缀排序+并查集,后缀排序之后就求出了两两相邻字典序后缀之间的相似程度,就可以从n-1到0扫一遍,每次用相似程度为i的两对更新答案。
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 600050
#define fr first
#define sc second
#define ll long long
pair <int,int> a[N];ll ans,Ans[N][2],s[N],ma[N],mi[N],tot,INF;
int n,m,fa[N],c[N],d[N],sx[N],sy[N],li[N];char b[N];
inline int Read()
{
int x=0;char y;bool z=0;
do y=getchar(),z|=y=='-'; while (y<'0'||y>'9');
do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
return z?-x:x;
}
int Find(int x)
{return fa[x]==x?x:(fa[x]=Find(fa[x]));}
inline ll max(ll x,ll y)
{return x<y?y:x;}
inline ll min(ll x,ll y)
{return x<y?x:y;}
void aa(int x)
{
memset(c,0,sizeof(c));memset(d,0,sizeof(d));
memset(sy,0,sizeof(sy));int ri=x;
for (int i=0;i<x;i++) sy[i]=n-i-1;
for (int i=0;i<n;i++)
if (sx[i]>=x) sy[ri++]=sx[i]-x;
for (int i=0;i<n;i++) c[li[i]]++;
for (int i=1;i<=n;i++) c[i]+=c[i-1];
for (int i=0;i<n;i++)
sx[c[li[sy[i]]-1]++]=sy[i];
d[sx[0]]=1;
for (int i=1;i<n;i++)
d[sx[i]]=d[sx[i-1]]+1-(li[sx[i]]==li[sx[i-1]]&&
li[sx[i]+x]==li[sx[i-1]+x]);
for (int i=0;i<n;i++) li[i]=d[i];
}
int main()
{
n=Read();
scanf("%s",b);
for (int i=1;i<=n;i++) ma[i]=mi[i]=Read(),fa[i]=i,s[i]=1;
for (int i=0;i<n;i++) c[b[i]]++;
for (int i=1;i<=256;i++) c[i]+=c[i-1];
for (int i=0;i<n;i++)
sx[c[b[i]-1]+(d[b[i]]++)]=i,
li[i]=c[b[i]];
for (int i=1;i<n;i <<= 1) aa(i);
int now=0,ri=0;ans=INF=-(ll)(1e18);
for (int i=0;i<n;i++)
{
now-=now>0;
if (li[i]==1) continue;
while (i+now<n&&sx[li[i]-2]+now<n&&b[i+now]==b[sx[li[i]-2]+now]) now++;
a[++ri].fr=now;a[ri].sc=i+1;
}
sort(a+1,a+n);ri=n-1;
for (int i=n-1;i>=0;i--)
{
while (ri&&a[ri].fr==i)
{
int k=Find(a[ri].sc),l=Find(sx[li[a[ri].sc-1]-2]+1);
if (k!=l)
fa[k]=l,ans=max(ans,ma[k]*ma[l]),
ans=max(ans,mi[k]*mi[l]),
ma[l]=max(ma[l],ma[k]),mi[l]=min(mi[k],mi[l]),
tot+=s[l]*s[k],s[l]+=s[k];
ri--;
}
Ans[i][0]=tot;Ans[i][1]=ans;
}
for (int i=0;i<n;i++)
printf("%lld %lld\n",Ans[i][0],!Ans[i][0]?0:Ans[i][1]);
return 0;
}
T3这种凶残的题还是不准备现在填坑了= =
2-SAT 乱水
看了下2-SAT,然后随便水了两道题
POJ3207 这个段哥当时讲过,然而我当时十分Naive。。
这题良心到Tarjan都不用写直接写个BCJ就好了因为连边的特殊性
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 1050
int n,m,ln[N][2],fa[N],ri,ss=1,st=0,ans=0;
inline int Read()
{
int x=0;char y;
do y=getchar(); while (y<'0'||y>'9');
do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
return x;
}
inline bool Intersect(int x,int y)
{return ln[x][0]>ln[y][0]&&ln[x][0]<ln[y][1]&&ln[x][1]>ln[y][1];}
int Find(int x)
{return fa[x]==x?x:(fa[x]=Find(fa[x]));}
int main()
{
n=Read();m=Read();
for (int i=1;i<=m;i++)
{
ln[i][0]=Read();ln[i][1]=Read();fa[i]=i;fa[i+m]=i+m;
if (ln[i][0]>ln[i][1]) swap(ln[i][0],ln[i][1]);
}
for (int i=1;i<m;i++)
for (int j=i+1;j<=m;j++)
if (Intersect(i,j)||Intersect(j,i))
fa[Find(i)]=Find(j+m),fa[Find(i+m)]=Find(j);
for (int i=1;i<=m;i++)
if (Find(i)==Find(i+m)) {ans=-1;break;}
if (ans) puts("the evil panda is lying again");else
puts("panda is telling the truth...");
return 0;
}
POJ3678
Tarjan然后求个强连通分量,如果i和i'在同一强连通分量内就肯定是无解的。
至于怎么证并不知道= =,而且这题还没有对称性
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 2050
#define M 3000050
int fi[N],c[M][2],low[N],n,m,ss,ri,li[N],cl[N],st,ln[M][2];
int s[N];bool b[N];char d[20];
inline int Read()
{
int x=0;char y;
do y=getchar(); while (y<'0'||y>'9');
do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
return x;
}
inline void Line(int x,int y)
{c[++ss][0]=y;c[ss][1]=fi[x];fi[x]=ss;ln[ss][0]=x;ln[ss][1]=y;}
void DFS(int x,int y)
{
int k=++ri;li[ri]=x;low[x]=y;b[x]=1;
for (int i=fi[x];i;i=c[i][1])
if (b[c[i][0]])
low[x]=min(low[x],low[c[i][0]]);else
if (!low[c[i][0]])
DFS(c[i][0],y+1),low[x]=min(low[x],low[c[i][0]]);
if (low[x]==y)
{
st++;
for (int i=k;i<=ri;i++)
b[li[i]]=0,cl[li[i]]=st;
ri=k-1;
}
}
int main()
{
n=Read();m=Read();
for (int i=1;i<=m;i++)
{
int q=Read()+1,w=Read()+1,e=Read();
scanf("%s",d);
if (d[0]=='A'&&!e)
Line(q,w+n),Line(w,q+n);else
if (d[0]=='A')
Line(q+n,q),Line(w+n,w);else
if (d[0]=='O'&&!e)
Line(q,q+n),Line(w,w+n);else
if (d[0]=='O')
Line(q+n,w),Line(w+n,q);else
if (e)
Line(q+n,w),Line(q,w+n),Line(w,q+n),Line(w+n,q);else
Line(q,w),Line(w,q),Line(q+n,w+n),Line(w+n,q+n);
}
for (int i=1;i<=n*2;i++)
if (!low[i]) DFS(i,1);
for (int i=1;i<=n;i++)
if (cl[i]==cl[i+n])
{puts("NO");return 0;}
puts("YES");
return 0;
}
还有输出字典序最小解的就是暴力O(nm)DFS
任意解的就是在上面Tarjan的基础上拓扑排序一下,每次选一个出度为0的未染色点,把它的对应点i和i的所有前驱全部标为不选。因为有之前那个性质所以并不用拓扑排序过程中无需判无解。
计划
感觉最近虽然也很废,但是至少不是像之前那样混日子了,现在每天都还是有所收获的
而且从机房里面搬出去之后效率也应该能提升蛮多吧。只不过感觉真心不能作死熬夜了= =不然第二天上午就会睡掉
这周还有5天,先补充姿势水平,把一些还没学的常用的东西比如2-SAT什么什么的学掉。然后USACO Gold还可以刷一些。有时间去填一些比如NOI或者一些BZOJ上面比较经典的系列的坑。按现在这种状态感觉去开刷CF还是很虚的。
然后下周尽量跟上*利的进度吧。
以我的能力还有这种拙计智商,不知道能走多远,走到哪里呢。只是真心不想搞高考啊,而且我还有很多事情想要做的,所以尽量努力吧。
UPD 9.18 好萎啊不知道为什么,感觉吧总是很容易没有动力。
UPD 9.20 我们学校吧根本就没有一个搞OI的氛围,天天有人在摆。他们是没有考虑过省选之后的事情么,或者是真的觉得自己不用努力就能够混到一个很好的成绩么。而且风气也很奇怪,成天D人或者是BB。感觉这本来就不是什么好东西为什么不能控制一下。D人也本来是为了提醒别人但是我不懂成天D人有什么意义。
VFK曾经说过一句话:OI并不是酱油学科。现在想来很有道理。本来就是没有人能够随随便便成功的
只能够希望新一届高一能够不要像我们这样吧
BZOJ 2716/2648
膜了一发K-D Tree这种高端东西
感觉比想象中好写。
但是不用指针就会T,用了指针就跑的飞起只有10+s感觉实在不科学= =
这个版本并没有套上暴力重建,也懒得套暴力重建了= =
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 1000050
#define INF 0x7f7f7f7f
struct Point
{int x,y,x1,y1,x2,y2;Point *c[2];};
int n,m;
bool cmp(const Point &x,const Point &y)
{return x.x<y.x;}
bool cpm(const Point &x,const Point &y)
{return x.y<y.y;}
inline int Read()
{
int x=0;char y;
do y=getchar(); while (y<'0'||y>'9');
do x=x*10+y-'0',y=getchar(); while (y>='0'&&y<='9');
return x;
}
struct KD_Tree
{
int st,ans;Point now,*gf,t[N];
inline int Minimum(Point *x,Point y)
{
int k=0;
if (y.x<x->x1) k+=x->x1-y.x;else
if (y.x>x->x2) k+=y.x-x->x2;
if (y.y<x->y1) k+=x->y1-y.y;else
if (y.y>x->y2) k+=y.y-x->y2;
return k;
}
inline int Dis(Point *x,Point y)
{return abs(x->x-y.x)+abs(x->y-y.y);}
inline void Get(int x)
{
t[x].x=t[x].x1=t[x].x2=Read();
t[x].y=t[x].y1=t[x].y2=Read();
}
inline void Update(Point *x,Point *y)
{
x->x1=min(x->x1,y->x1);
x->x2=max(x->x2,y->x2);
x->y1=min(x->y1,y->y1);
x->y2=max(x->y2,y->y2);
}
Point *Build(int x,int y,int z)
{
int k=x + y >> 1;
if (x>=y) return x==y?&t[x]:NULL;
nth_element(t+x,t+k,t+y+1,z?cmp:cpm);
t[k].c[0]=Build(x,k-1,!z);
t[k].c[1]=Build(k+1,y,!z);
if (t[k].c[0]!=NULL) Update(&t[k],t[k].c[0]);
if (t[k].c[1]!=NULL) Update(&t[k],t[k].c[1]);
return &t[k];
}
void Insert(Point *x,int y,int z)
{
int k=(z&&x->x<=t[y].x)||(!z&&x->y<=t[y].y);
if (x->c[k]==NULL)
x->c[k]=&t[y];else
Insert(x->c[k],y,!z);
Update(x,x->c[k]);
}
void query(Point *x,int y)
{
int k=(y&&x->x<=now.x)||(!y&&x->y<=now.y);
ans=min(ans,Dis(x,now));
if (x->c[k]!=NULL&&Minimum(x->c[k],now)<ans)
query(x->c[k],!y);
if (x->c[!k]!=NULL&&Minimum(x->c[!k],now)<ans)
query(x->c[!k],!y);
}
int Query(int y,int x)
{
ans=INF;now.x=x;now.y=y;
query(gf,0);return ans;
}
} A;
int main()
{
n=Read();m=Read();
for (int i=1;i<=n;i++)
A.Get(i);
A.gf=A.Build(1,n,0);A.st=n;
while (m--)
if (Read()-1)
printf("%d\n",A.Query(Read(),Read()));else
A.Get(++A.st),A.Insert(A.gf,A.st,0);
return 0;
}
USACO Gold题填(bei)坑(nue)计划
做起来果然比Silver带感多了,更能体现我智商癌的本质。
感觉自己还真是萎废,这种题还做得一卡一卡的
BZOJ1230 线段树
BZOJ1231 一开始没想出来,后来发现是状压DP
BZOJ1233 好神的贪心+DP,首先一个草堆要最高必须要底层最瘦。证明好像其它地方有,然后就可以DP了,记录第i个到第n个最优情况下有多瘦、有多高。一开始没注意底层宽度不一定具有单调性,然后就wa了,后来看了一下式子发现要用单调队列优化。
BZOJ1571 直接DP
BZOJ1572 首先把工作按照截止日期排个序,然后从后往前枚举时间点用个set什么的每个时间点选取最大价值的。
BZOJ1574 贪心,把跟那些点相邻的点都打上标记,最后从起点DFS能到达多少没打标记的点,最后用n-cnt就是答案了
BZOJ1575 预处理出每个区间的误差值和,最后DP一下
BZOJ1577 按照右端点排序,然后依次放就好了。
BZOJ1578 一个东西第i天买进第j天卖出相当于每天都买入卖出,所以直接算出每天最多有多少钱,也就是每天背包一下。
BZOJ1581 DP,设ai,j代表前i+j位放了i个0和j个1,有多少种方案。算第k大也是扫一遍什么的就好了
BZOJ1583 直接算,只不过题意是真的艹蛋
BZOJ1584 发现k最大只能是200,所以直接DP好了
BZOJ1585 好久没做网络流的题目了裸的最小割没看出来TAT
BZOJ1589 BFS
BZOJ1590 trie
BZOJ1592 离散化+DP
BZOJ1593 我写的splay,调起来极其带感
BZOJ1596 贪心
BZOJ1597 斜率优化
BZOJ1692 后缀数组
BZOJ1694 DP,算的是这段距离对答案的总贡献所以并没有后效性
BZOJ1697 想到了正解一开始并不敢写= = 对于每个置换环要不就用其中最小的节点在上面移,要不就把全局最小的移进来。
BZOJ1699 重题了
BZOJ1702 前缀和一下然后差分一下然后排个序
BZOJ1703 猜了个结论就是不能确认的关系的对数
BZOJ1704 乱搞
BZOJ1705 DP,因为增加的高度不会太大,但是不能只开到10,我反正开到20才过
BZOJ1706 矩阵快速幂
BZOJ1707 乱搞
BZOJ1708 背包
BZOJ1709 首先找到一个有对手的格子,答案就只能放在它的八个方向的格子上
BZOJ1710 DP
BZOJ1711 TAT果然网络流荒废太久了都不记得了,建图S->F->N->D->T
BZOJ1712 乱搞+快速幂
BZOJ1690 分数规划+二分答案+找负环,感觉好神啊。一开始还没看到路线是个环,感觉无论如何都不能理解= =
BZOJ1604 好神的规律题啊,可以看这里。
BZOJ1598 K短路,然而因为是拓扑图所以可以用A*算法。记得我以前是看过鼎爷的论文的然而已经忘得差不多了= =