前言
为什么总结只有1和8,没有2、3、4、5、6、7呢?因为只有这两次是AK才有时间写啊!
a路径
题目很长,简单一点来说就是一个二维平面坐标系中有N个整数点,主人公Bessie,每一步可以走上下左右四个点,要求你从(0,0)出发,走过所有给定的点,且结束点是任意一个给定的点,问是否有一个方案所有步数之和的奇偶性为P。
分析
这题作为一道签到题,想一下就会发现这道题唯一的条件“步数之和”只与结束点有关,与其他都是没有关系的,所以对于每一个点,将它的坐标x和y加起来判断奇偶即可。
程序
就不贴了吧。
b冠军
有N个拳手参加比赛,
分析
这题什么乱七八糟的方法(其实大部分都是在N!的方法上改进的)我都想过,最后发现除了状压加搜索优化以外,没有什么更好的方法。
f[i][j][P]表示到了第i轮比赛,胜者是第j个拳手,P表示哪几位拳手参加了比赛。
搜索dfs(k,s,w,p)表示搜索到前k个拳手已经搜索完,一共有s个拳手被选中,其中胜利的是第w个拳手,这s个拳手是这p个。
其中第w个拳手一定要输给第j个拳手,且第j个拳手不能在这s个拳手中。
为了节省时间,一开始我还把有效的P找出来,但现在分析一想,并没有优化什么太多时间复杂度。
这题如果时间开1s确实很卡。。
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <cstring>
#include <vector>
#include <time.h>
using namespace std;
long long f[6][20][70000];
int N,w,P,n,m,sl[20],b[20][70000],a[20][20],tep[20];
char st[20];
void dfs(int k,int s,int p,int lw) {
if ((s<<1)==(1<<N)) {
if (lw==-1) return;
f[N][w][P]+=f[N-1][lw][p]*f[N-1][w][P-p]*2;
return ;
}
for (int i=k;i<=(1<<N);i++)
{
if (lw==-1&&a[w][tep[i]]) dfs(i+1,s+1,p|(1<<tep[i]),tep[i]);
if (tep[i]!=w) dfs(i+1,s+1,p|(1<<tep[i]),lw);
}
}
int main(){
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
scanf("%d",&n);
for (int i=0;i<n;i++){
scanf("%s",&st);
for (int j=0;j<n;j++)
if (st[j]=='N') a[i][j]=0;else a[i][j]=1;
}
int l=1<<n;
for (register int i=0;i<l;i++) {
int i1=i,s=0;
while (i1>0) {++s;i1-=i1&-i1;}
if (s==2) s=1; else
if (s==4) s=2; else
if (s==8) s=3; else
if (s==16) s=4; else
s=0;
if (s!=0){
sl[s]++;b[s][sl[s]]=i;
}
}
for (int i=0;i<n;i++)
for (int j=0;j<n;j++)
if (a[i][j]) f[1][i][(1<<i)+(1<<j)]=2;
if (n==2) m=1;if (n==4) m=2;if (n==8) m=3;if (n==16) m=4;
for (int i=2;i<=m;i++)
for (register int j=1;j<=sl[i];j++) {
int cnt=0;
for (int k=0;k<n;k++)
if (b[i][j]&(1<<k)) tep[++cnt]=k;
P=b[i][j];N=i;
for (int k=1;k<=cnt;k++){
w=tep[k];
dfs(1,0,0,-1);
}
}
for (int i=0;i<n;i++)
printf("%lld\n",f[m][i][(1<<n)-1]);
return 0;
}
中间的if (n==2) m=1;if (n==4) m=2;if (n==8) m=3;if (n==16) m=4;
为什么用log(n)/log(2)算不出来,求大神解答。
小结一波
这道题的确卡了我非常多时间,首先一开始我认为状态转移需要很多时间,粗略一点就是 216 ,就算是运用组合数减少转移状态的时间也不会好到哪里去。我没有算C,导致我以为 C816 很大,不比 216 少多少,算出来也证明只比 216 少6倍,但在这种卡常数的题中,少6倍就足够了。
C指纹
给你n组四元组{a,b,c,d},其中如果每一个四元组A中的元素,有至少3个比另一个四元组B中的元素要小,则认为四元组B是“累赘”的。问有哪几组四元组是累赘的。
分析
由于第二题花了比较多的时间,所以这一题我想得并不是很深入。
首先n很大,就已经卡掉了很多譬如网络流之类的乱七八糟的算法。
对于本题而言,
nn√
和
nlog2n
的时间复杂度都是可以接受的,很容易想去数据结构和分块方面。其中分块我没想过,也不好想,所以我直接想数据结构方面了。(毕竟现在的oier数据结构都学得很好,当然蒟蒻我只会线段树)
一共有四维数据。首先肯定要先对第一维排序,这样子就可以减少一维数据的干扰。
然后问题来了,对于每一组四元组,你可以有两种方法进行处理,一种是判断这个四元组是不是“累赘”,另一种是用这个四元组来判断前面的四元组是不是“累赘”。由于第二种方法具有不确定性,相对来说第一种方法看上去简单很多。(对于暴力来说,两种方法并没有什么区别)
然后想如何不用排序的方式来判第二维的大小,然后就想到以数组下标为基础的桶排,然后用数据结构询问比第二维小的有没有就可以了。
然后考虑第三维,看着这个数据结构,我很惊奇地发现,这颗线段树里空空如也,我们就可以把第三维记进去。
既然这样,那也顺便把第四维也记进线段树里面。
总的来说,就是以第一维排序,那么先加入数据结构里面的a一定比当前a要小。然后在在线段树中1~b中找最小的c和d,最后判断即可。
这种方法还有一个缺陷,就是某一个四元组是“累赘”的,但a比较小,你没有把它视为“累赘”,所以这样做完一次以后,将c、d和a、b交换再做同样的操作即可。
程序
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <cmath>
#include <cstring>
#include <vector>
#include <time.h>
using namespace std;
struct node{
int a,b,c,d,e;
}a[100010];
int T[2][400010],n;
bool ans[100010];
bool cmp(node A,node B) {
return A.a<B.a;
}
void update(int p,int ro,int L,int R,int x,int v) {
if (L==R&&R==x) {T[p][ro]=v; return;}
if (L>x||R<x) return;
int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
update(p,zuo,L,mid,x,v);
update(p,you,mid+1,R,x,v);
T[p][ro]=min(T[p][zuo],T[p][you]);
}
int query(int p,int ro,int L,int R,int le,int ri) {
if (le<=L&&R<=ri) return T[p][ro];
if (R<le||L>ri) return n+1;
int mid=(L+R)>>1,zuo=ro<<1,you=zuo+1;
int A=query(p,zuo,L,mid,le,ri),
B=query(p,you,mid+1,R,le,ri);
return min(A,B);
}
void work(){
sort(a+1,a+1+n,cmp);
for (int i=0;i<=4*n;i++) T[0][i]=T[1][i]=n+1;
update(0,1,1,n,a[1].b,a[1].c);
update(1,1,1,n,a[1].b,a[1].d);
for (int i=2;i<=n;i++) {
int A=query(0,1,1,n,1,a[i].b),
B=query(1,1,1,n,1,a[i].b);
if (A<a[i].c||B<a[i].d) ans[a[i].e]=false;
update(0,1,1,n,a[i].b,a[i].c);
update(1,1,1,n,a[i].b,a[i].d);
}
}
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) ans[i]=true;
for (int i=1;i<=n;i++) {
scanf("%d%d%d%d",&a[i].a,&a[i].b,&a[i].c,&a[i].d);
a[i].e=i;
}
work();
for (int i=1;i<=n;i++)
{
swap(a[i].a,a[i].c);
swap(a[i].b,a[i].d);
}
work();int cnt=0;
for (int i=1;i<=n;i++)
if (!ans[i]) cnt++;
printf("%d\n",cnt);
for (int i=1;i<=n;i++)
if (!ans[i]) printf("%d\n",i);
return 0;
}
小结
这题我联想起gdoi2016的某一天的第二题,做暴力并用抽屉原理来优化,但好像本题不是用这个方法。