niop准备 题目分类

来源:互联网 发布:牛顿法求根 c语言 编辑:程序博客网 时间:2024/05/31 06:23

【sg函数】
new oj 1755 分裂游戏 :
将每一个单独的石子看成一个游戏; sg(i)表示一个石子距离结束位置i 有堆石子远 ;类比一个有向无环图;每次可以从起点往后面走;这样走到不能走的就输了(sg[1]=0)//1=n-n+1;
这样最后再把所有的sg^起来;
如: 1 4 3
sg[3]^sg[2]^sg[2]^sg[2]^sg[2]^sg[1]^sg[1]^sg[1]

【倍增法】
new oj 2347 国旗计划 :
预处理出f[i][j] 表示第i个人用了2^j个区间后走到的区间,同时s[i][j]记录这段路径长度,查询的时候类似树上倍增求lca一样往下枚举j统计答案即可。

【半平面交】
new oj 2348 小凸想跑步 :
先把使得每个三角形和第一个三角形面积相等的直线求出,做一遍半平面交即可。

【网络流】
new oj 1754 80人环游世界 :
网络流spfa+上下界。由于每个城市有一个固定的访问量,故想到将城市拆分成两个点,中间的连边用上下界都是vis[i]来卡死。这样其他边不变,跑一遍spfa即可。

new oj 2405 orzwwx :
很显然的网络流,构图很简单(网络流就这个难),然后裸地跑一边无源汇的上下界。判断是否可行。然后删掉超级源点和超级汇点,(T->S的inf边可删可不删)。再进行一次增广看最多的解就可以了。

new oj 1546 导弹防御塔 :
很明显的网络流,只是图很难构建,因为所有的防御塔攻击时间是可以相交的。所以怎么办呢?可以知道ans>=max*(each (t1*k+t2*(k-1)+dist[i][j]))对于每个攻击对象的dist+总的冷却时间+总的发射时间是<=ans的。所以就二分答案。然后每个防御塔拆成m个点表示第几个发射,这样最后直接跑一边二分图最大匹配就好了。注意加边不要重叠。一开始老是往修车那边靠,结果爆0。其实网络流24题里面有些就是二分答案的,下次注意。

【dp】
new oj 2177 Sailing Race :
首先考虑不经过第一条边的情况,那么用f[i][j][0]表示顺时针走从i出发第二步到j可以经过的最多路径数;
逆时针同样,转移方程为 f[i][j][0]=max(f[i][j][0],1+max(f[k][j][0],f[k][i][1]));
如果有经过第一条边,则必定在一边是贴着圆走然后经过第一次的路径再到另一半的圆中去随便走。后面的一部分第一问已经求了。所以再用t[i][j]表示从i到j贴着圆走能经过几个点,然后则这个情况下让分隔线尽量地靠近j点能使后一部分尽可能大。注意for的顺序。

new oj 2404技能点(skillpoint) :
分析题目可得到这样的性质:每个技能必定是尽量加满的最优,也就是最后的情况一定是所有要求的技能+满流技能+至多一个不满的技能。
所以预处理把“至多一个不满的技能”当成一个要求技能;注意这个很小可以状压。f[i][j]表示前i个技能j是满足技能的状压;

for(int i=1;i<=num;i++)    {        now=i&1;        pre=now^1;// 注意要用滚动数组节省空间;        for(int j=0;j<=S;j++) f[now][j]=0;        for(int j=0;j<L;j++)        {            int cnt=0;            for(int t=1;t<=S;t++)                if(j&(1<<(t-1))) f[now][j]=max(f[now][j],f[pre][j^(1<<(t-1))]+a[pos[i]][limit[t]]),++cnt;            if(i>cnt) f[now][j]=max( f[now][j],f[pre][j]+(i-cnt<=all? a[pos[i]][k]:0));            }       } 

new oj 1275 股票交易 trade :
可以想到这样的dp式子:
f[i][j]表示第i天剩余j个股票获得的最大收益。
f[i][j]=max(
f[i-1][j] , //昨天留下。
-j*inval[i],//今天刚买。
f[i-w-1][now]+(now-j)*inval[i]。// 上次交易+刚买;
f[i-w-1][now]+(now-j)*outval[i]。//上次交易-刚卖。
)
这样话需要每个j都要枚举now,用单调队列搞一下就可以。
部分代码:

 for(int i=1;i<=T;i++)    {        for(int j=0;j<=it[i];j++) f[i][j]=-j*il[i]; // 这个要加;         for(int j=0;j<=Maxp;j++) f[i][j]=max(f[i][j],f[i-1][j]);        int pre=i-W-1;        if(pre>=0)        {            head=1; tail=2;            q[1]=0;            for(int j=0;j<=Maxp;j++)            {                while(head<tail && q[head]<j-it[i]) head++;                int now=q[head];                if(head<tail)                  f[i][j]=max(f[i][j],f[pre][now]+(now-j)*il[i]);                while(head<tail && f[pre][j]+j*il[i]>f[pre][q[tail-1]]+q[tail-1]*il[i]) tail--;                q[tail++]=j;}

new oj 1110 bookshelf :
这种关于连续区间的dp一般是枚举断点,f[i]=f[j]+max(from h[j] to h[i])注意到从j到i中的高度是一个单调递减的序列,如5,3,4中3不影响答案,因为3可以并到5的情况中去,答案一定更优。
所以这里用一个单调队列维护一下,然后开一个大根堆就可以。

new oj 1770 nochange :
很显然的dp,总的只有16个银币,所以状压一下随便转移就好了(考场上答案输出-1我输出0被扣30):

 for(int i=Max-1;i>=0;i--)    {        for(int j=1;j<=k;j++)            if((i&(1<<(j-1)))==0)            {                int pre=i^(1<<(j-1));                int x=get_pos(f[pre],coin[j]);//二分找x最大值。                 f[i]=max(x,f[i]);               }       }

【数论】
new oj 2010 老人 :
首先考虑答案ans:分解质因数以后得到的是p1^k1*p2^k2……pn^kn; 对于一个p的贡献是(1+p^1+p^2+p^3……p^k1)提出(1+p)(1+p^2+p^4+…)则(1+p)能被2^m整除,后一项提出(1+p^2)(p^4+p^6…)
(1+p^2)②能被2^t整除,则p=2^m-1代入②得2(2^(2n-1)-2^(n-1)+1)| 2^t; 显然不满足题意;因此ans分解质因数以后只有质因数的一次方且质因数满足=2^k-1;为2,3,5,7,13,17,19,31;所以可以把这几个数状压起来;判断每个读入的数分解后是不是由这几个数的一次方组成;然后再更新ans。

new oj 1933 墨墨的等式 :
对于这个多项式,首先容易想到的应该是答案为ans(Bmax)-ans(Bmin-1) 然后观察多项式写成如下形式 a1*x+k=B(任意一个整数);k为后面那些的结果(与0-a1-1一一对应) a1为最小的系数(跑起来快)。如果能够对于每一个的k,都能找到最小的x使得等式成立,那么就可以求出方案数,所以把0-(a1-1)看成a1个点,每个点用a2-an进行连边i->(a[j]+i)%a[1]连一条边权为a[j]的边,那么最后跑一边最短路求出来的就是组成k的最小值,然后就可以

 Bmin--;    for(int i=0;i<a[1];i++) if(dist[i]<=Bmax) ans1+=(LL)(Bmax-dist[i])/LL(a[1])+1;    for(int i=0;i<a[1];i++) if(dist[i]<=Bmin) ans2+=(LL)(Bmin-dist[i])/LL(a[1])+1;    printf("%lld\n",ans1-ans2);

【线段树】
new oj 1710 觉醒力量 :
由于14689=11*13*17*19; 联想到这个等式 f(x)%p==f(x+p)%p;
其中p是质数; 所以我们只要知道一个询问对于11,13,17,19,的结果是多少;就可以用中国剩余定理求出来。而对于每个数。每次计算会超时(插入的时候)。所以建立四颗线段树分别预处理出所有的方案即可。
(中国剩余定理的简单计算)
{
如对于某个x;
被3除余1,被4除余2,被5除余4,这个数最小是几?
题中3、4、5三个数两两互质。
则〔4,5〕=20;〔3,5〕=15;〔3,4〕=12;〔3,4,5〕=60。
为了使20被3除余1,用20×2=40;
使15被4除余1,用15×3=45;
使12被5除余1,用12×3=36。
然后,40×1+45×2+36×4=274,
因为,274>60,所以,274-60×4=34,就是所求的数。
}

【矩阵乘法】
new oj 1718 杰杰的女性朋友 ( travel ) :
本题中容易想到的一种解法就是对于矩阵a[i][j]表示从i到j走一步的方案数,然后用黑科技(外加鬼节点)来记录中间的方案数跑一边矩阵快速幂就可以。然而只有五十分(矩阵太大);所以想办法;
首先发现每两个city之间的路径数要有k种计算,因此多加k个节点。向每两个城市连边。因此从i到j可以看成是i->k->j;而k中两点所走的路径是多少呢?k1->k2必然要经过别的城市,事实上是所有的城市,所以

for(int i=1;i<=K;i++)        for(int j=1;j<=K;j++)            for(int z=1;z<=n;z++)                {                    map[i][j]+=(LL)ind[z][i]*out[z][j];                    if(map[i][j]>p) map[i][j]%=p;                    }// 而鬼节点的连边是这样的:for(int i=1;i<tot;i++) mi[i][tot]=ind[T][i];// 结果计算: for(int i=1;i<tot;i++) ans=(LL)(ans+base[i][tot]*out[S][i])%p;   

PS:注意+1-1的问题和当走0,1步的时候,这里在考场上本来是50结果被卡了40分。

new oj 1813 reading :
很显然的矩阵乘法,像这种方案数超级多显然不能一个一个算而且要保存中间过程的当然首选矩阵。比较蛋疼的就是有一个差异值,然而发现差异值最多是5。所以进行拆点,每个点分为五个点,矩阵构法是这样的:

void build(){//f[i][j]如果没有特殊要求为1,有的话就是差异值。    for(int i=0;i<imax;i++)        for(int j=0;j<imax;j++)        {            int now=f[i][j];            int nowi=i*5+now; int nowj=j*5+1;            base[nowi][nowj]=1;        }    for(int i=0;i<imax;i++)        for(int j=1;j<=4;j++)        {// 每个字母的五个分身连边。            int nowi=i*5+j; int nowj=i*5+j+1;            base[nowi][nowj]=1;        }    base[131][131]=1;//鬼节点    for(int i=0;i<26;i++) base[i*5+1][131]=1;    for(int i=1;i<=131;i++)        for(int j=1;j<=131;j++) mi[i][j]=base[i][j]; }//统计部分:for(int i=0;i<26;i++)     { ans+=base[i*5+1][131]; if(ans>p) ans%=p;}

【差分约束系统】
new oj 1534 Intervals :
主要是普及算法题。如果知道了差分约束系统就很简单。首先设一个f[i]表示Z集合中小于i的数有几个。然后题目条件[l,r]中至少a个数可转化为:f[r]-f[l-1]>=a; 得到一系列方程组。转化成图论,最后求f[Max]-f[Min-1]的Max。跑一遍spfa即可。注意隐藏条件:0<=f[i+1]-f[i]<=1。
(差分约束系统)
{
如给出三个不等式,b-a<=k1,c-b<=k2,c-a<=k3,求出c-a的最大值,我们可以把a,b,c转换成三个点,k1,k2,k3是边上的权,如图
这里写图片描述
由题我们可以得知,这个有向图中,由题b-a<=k1,c-b<=k2,得出c-a<=k1+k2,因此比较k1+k2和k3的大小,求出最小的就是c-a的最大值了。
}

【最短路】
new oj 1533 robot :
详见乱射的图论之神,二维spfa

【数形结合】
new oj 1464 defend :
首先可以推出这样的公式:对于某一次需要的攻击,设此时队伍长度是len,前缀血量是sum
for(int i=len;i>=1;i--)
ans=max(ans,(sum[len]-sum[i-1])/(d[len]+(len-i)*d));

这样枚举的话是O(n^2)的,只能百分四十;但是可以通过观察上式发现分子写成(d[len]+len*d- i*d)因此看成是两个点A(d[len]+len*d,sum[len])和B(i*d,sum[i-1])这样求出的斜率最大就是答案,对于每个A,可能成为答案的是下凸壳上的点,因此维护一个下凸壳,查找的时候发现斜率是一个单峰函数,用三分即可。

【KMP】
new oj 1062 sub :
这道题是Match的简化版,只要在模板串上求一遍fail指针即可,然后从后往前枚举,i的sum累加到fail[i]上(初始一开始sum为1)在正向扫一遍就可以。具体可以看Match有以前的注解。

【行列式】
new oj 2405 orzcyr :
很关键的思维突破点在于题目中的不相交处理。考虑两条路径,如果把他们的焦点后的路径反过来,也就是说A在焦点后走了B的路,反过来B走A的路,那么最后答案抵消不影响。所以我们预处理出一个矩阵:s[i][j]存放i->j的方案数。然后对于每一种j的排列计算答案(每种方案数连乘)再相加即可(正负号要注意)因此我们注意到这和行列式几乎就是一个东西。套用一下就可以。

int flag=1;    for(int i=1;i<=tot;i++)        for(int j=i+1;j<=tot;j++)        {            while(A[j][i])            {            // 注意这里的消元方法,由于p不是一个质数,所以采用            //了类似gcd的过程。由于交换两行行列式的值会相反,            //用flag体现。                int cha=A[i][i]/A[j][i];                for(int z=i;z<=tot;z++)                {                    A[i][z]=(A[i][z]-(LL)A[j][z]*cha)%p;                    swap(A[i][z],A[j][z]);                  }                flag=-flag;             }

【Tarjan】
new oj 1536 network:
详见乱射的图论之神详解,这里不再赘述。

【莫队】
new oj 1667 :
普及算法题,莫队。

【思维杂题】
new oj 1179 高楼 skyscraper :
容易分析得出这样的结论:最高的楼一定是看得见的;
如果我们能够知道对于每一个最高的楼的位置的左右方案数;就能知道答案。
分析对于高楼在i位置。首先左边高度有C(n-1,i-1)的选择方式。然而有效的只有他们的相对高度。所以用f[i][j]表示i栋楼j个上升的方案数,下降是一样的(倒着数)。所以得出:

f[i][j]=f[i-1][j-1]+(n-1)f[i-1][j];// 每次考虑插入一个最小的,一种情况是插在最开头,上升序列数+1,一种是插在(n-1)个位置,不影响上升数量(被挡住)。

所以只要求c[n-1][1]~c[n-1][n-1],和要求长度长度的f[1][L]~f[i][L](下降一样)即可。最后统计式:

for(int i=l;i<=n-r+1;i++)    ans+=((LL)c[i-1]*L[i-1]%Mod)*R[n-i];

new oj1365 fly :
像这种区间的取法很多时候是按照右端点排个序就ok,查询和修改的时候用线段树维护一下就好了(考场上自己定义了一个比较方式还70,讲评后删掉一段代码加上一个sort就A了。)
至于为什么这样排序是对的可以这样理解:
每次我们都是使得车上的人尽量快的下车,好腾出空间给下一些人。具体可以自己画图理解,这里不再赘述。

new oj 2407orzzcz :
这个题目一看我就一直向sg靠结果当然是错的。首先从小的分析:

  • 只有一堆,先手必胜(全取);
  • 只有两堆且一样多,先手必败。(无论一开始先手怎么取,后手总能将这两堆石子变成一样多,也就是做对称的操作);
  • 两堆不一样多,显然就先手必胜。
  • 拓展:
    -奇数堆的时候,先手必胜。先手可以把最大堆的拿出来分配,将剩余配成两两相等的若干组。则后手就输了。
    -偶数堆的时候
    {
    若两两数量相等,先手必败。
    else 先手可以拿出最大的一堆分配,不要全取完,可以使得最后还是每两堆相等。先手必胜。
    }

new oj 1430 ballmachine :
这题的二操作可以把这条链上的第一个非空节点移到末尾即可,然后记录一下长度就是移动的数量。有一个地方就是无论怎么放都不会改变它的选择顺序,所以先用dfs预处理出这棵树的选择顺序,(注意中间不能用一个数组暂存,不然dfs会更改)。然后把还没有球的加入堆中,操作一一直取堆头即可,操作二用倍增法求一下祖先就可以。

new oj 1232 思维+树上最长链 :
通过观察可以发现,如果把三角形A通过一系列相邻的三角形能走到三角形B则AB一定存在一条线段经过它们中间所有的三角形。否则就是一个凹多边形。所有把每个三角形看成一个点,三角形相邻就在对应的点上进行连边,最后求一个树上最长链就可以。
树上最长链的求法:
先从任意一个点进行BFS,然后选取最后入队的那个点也就是使得u->v是从u出发的最长链,然后再从v出发做一遍BFS即可,具体证明可以自己用反证法进行证明。

mew oj 1626 zewi :
水题,直接字符串排序后找相邻的最长公共前缀就行。考场上还SB的写了一个后缀数组。

0 0
原创粉丝点击