西安铁一中2018暑期集训第二期模拟赛Day3题解
T1:拍照
- 题意:有
n
条船,每条船有四个参数
x,y,z,d
,表示船的头尾坐标,离岸边的距离以及方向
(d=−1
表示向左,
d=1
表示向右
)
。现在要在岸边选一个位置以与岸边成
45。
的方向拍照,并且可以等待适当的时间。每单位时间船会向自己的前进方向前进
1
格,求最多能拍到多少条完整的船。
-
n≤104,−106≤x<y≤106,1≤z≤106
.
- 题解:首先,对于一条船,如果它能被完整的观测到,那么必须要满足
y−z≤x+z
,这个很简单。但是船是有两个方向的,为此我们可以先固定一个方向的船不动,然后统计另一个方向的船。如果在
x
位置能观测到的向右的船,在
y
位置观测到的向左的船,且满足
x≤y
,那么在
[x,y]
中间的某个位置一定能够全部观测到。
- 所以将船按照
y−z,x+z
排序,用类似差分的方法从左往右,从右往左扫一遍即可。
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n,tot,T;
struct node{
int x,val,d;
}a[20005];
bool cmp(node r,node s)
{
if(r.x!=s.x) return r.x<s.x;
if(r.val!=s.val) return r.val>s.val;
return r.d>s.d;
}
int ans[20005],tans;
int main()
{
T=read();
for(int cas=1;cas<=T;cas++)
{
tot=0;tans=0;
n=read();
for(int i=1;i<=n;i++)
{
int x=read(),y=read(),z=read(),d=read();
int l=y-z,r=x+z;
if(l<=r)
{
a[++tot].x=l,a[tot].val=1,a[tot].d=d;
a[++tot].x=r,a[tot].val=-1,a[tot].d=d;
}
}
sort(a+1,a+1+tot,cmp);
int ret=0;
for(int i=tot;i>=1;i--)
{
if(a[i].d<0 && a[i].val==-1) ++ret;
ans[i]=max(ans[i+1],ret);
if(a[i].d<0 && a[i].val==1) --ret;
}
ret=0;
for(int i=1;i<=tot;i++)
if(a[i].d>0)
{
if(a[i].val==1) ret++;
tans=max(tans,ret+ans[i]);
if(a[i].val==-1) ret--;
}
printf("Case #%d:\n%d\n",cas,tans);
}
return 0;
}
T2:模式匹配
- 题意:略
- 题解:利用shift-and字符匹配算法,用bitset优化即可。shift-and算法的讲解见这里。具体做法就是对n个模式串都建立一个bitset即可。代码也很简单。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
int n;
char s[2000005],t[105];
int getid(char x)
{
if(x>='a' && x<='z') return x-'a';
else if(x>='A' && x<='Z') return x-'A'+26;
else if(x>='0' && x<='9') return x-'0'+52;
else return -1;
}
bitset<505> b[65],d;
int main()
{
while(gets(s))
{
for(int i=0;i<62;i++) b[i].reset();d.reset();
n=read();
for(int i=1;i<=n;i++)
{
int len=read();
scanf("%s",t);
for(int j=0;j<len;j++)
b[getid(t[j])][i]=1;
}
d[0]=1;
bool flag=0;
int m=strlen(s);
for(int i=0;i<m;i++)
{
if(getid(s[i])==-1) d.reset();
else d=d<<1&b[getid(s[i])];d[0]=1;
if(d[n]==1) flag=1,printf("%d\n",i-n+2);
}
if(!flag) puts("NULL");
getchar();
}
return 0;
}
T3:联通块
- 题意:有一张
n
个点
m
条边且无重边的无向图,求有多少个边集,使得删掉边集里的边后,图里恰好有
K
个连通块
-
1≤n≤K≤14,0≤m≤n∗(n+1)/2
- 题解:状压dp+枚举子集。
- 用
dp[i][state]
表示当前选的节点状态是state,并且形成了i个联通块的方案数。
- 那么状态转移方程为:
dp[i][S]=∑S′⊂Sdp[i−1][S−S′]∗dp[1][S′]
。
- 所以现在的问题转变为怎样求
dp[1][state]
。
- 用
cnt[state]
表示当前选的节点状态是state时的边数,
- 用
g[state]
表示当前选的节点状态是state时,选的节点不连通的方案数。
- 所以
dp[1][S]=2cnt[S]−g[S]
。
- 那么只要将
g[S]
求出来就行了。
- 显然,选的节点不连通一定是由多于1个联通块组成的,那么其中1个联通块可以表示为
dp[1][S′]
,剩下点之间的边数为
cnt[S−S′]
,如果要不连通,那么只要不连
S′
与
S−S′
之间的边即可,所以
cnt[S−S′]
这些边连不连都不影响结果,即:
g[S]=∑S′⊂Sdp[1][S′]∗2cnt[S−S′]
。
- 这道题至此解决,时间复杂度为
O(3n∗k)
。
- 注意枚举子集时,为了避免重复,要限制必须取最低位的元素
x
,枚举时可以先把
x
抠出来再安回去。
using namespace std;
typedef long long LL;
int read()
{
char c=getchar();int f=1,sum=0;
while(c<'0' || c>'9'){if(c=='-') f=-1;c=getchar();}
while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
return sum*f;
}
LL bit[257];
LL dp[15][maxs],cnt[maxs],g[maxs];
int T,n,m,k,a[25][25];
void init()
{
bit[0]=1;
for(int i=1;i<=256;i++) bit[i]=(bit[i-1]<<1)%mod;
}
int lowbit(int x){return x&-x;}
void solve(int cas)
{
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
n=read();m=read();k=read();
for(int i=1,u,v;i<=m;i++)
{
u=read();v=read();a[u][v]++;
if(u!=v) a[v][u]++;
}
for(int sta=1;sta<(1<<n);sta++)
{
int pos=0;
while(!(sta&(1<<pos))) pos++;
cnt[sta]=cnt[sta^(1<<pos)];
for(int j=pos;j<n;j++) if(sta&(1<<j))
cnt[sta]+=a[pos+1][j+1];
}
for(int sta=1;sta<(1<<n);sta++)
{
int t=lowbit(sta);g[sta]=0;
for(int s0=((sta^t)-1)&(sta^t);;s0=(s0-1)&(sta^t))
{
g[sta]+=dp[1][s0^t]*bit[cnt[sta^s0^t]];
if(!s0) break;
}
dp[1][sta]=(bit[cnt[sta]]-g[sta])%mod;
if(dp[1][sta]<0) dp[1][sta]+=mod;
}
for(int i=2;i<=k;i++)
for(int sta=1;sta<(1<<n);sta++)
{
int t=lowbit(sta);
for(int s0=((sta^t)-1)&(sta^t);;s0=(s0-1)&(sta^t))
{
dp[i][sta]+=dp[i-1][sta^s0^t]*dp[1][s0^t];
if(dp[i][sta]>=mod) dp[i][sta]%=mod;
if(!s0)break;
}
}
printf("Case #%d:\n%lld\n",cas,dp[k][(1<<n)-1]);
}
int main()
{
init();
T=read();
for(int cas=1;cas<=T;cas++)
solve(cas);
return 0;
}