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*算法。记得我以前是看过鼎爷的论文的然而已经忘得差不多了= =