POI专项A,B题解
POI专项A,B题解
A
T1: Flat Broken Lines
来源:POI1998 BZOJ2924
问题:
对于100%的数据,\(n,x,y\leq 3e4\)。
分析:
由于x,y较小,不用离散化
考虑将坐标系逆时针旋转45度,(\(x,y\))变成(\(x\cosθ-y\sinθ,x\sinθ+y\cosθ\)) θ=45°
因为都等于\(\frac{\sqrt{2}}{2}\),所以可以约去,坐标就转化为了(x-y,x+y)
转化之后就变成了每条链都单调不降。
所以求对于所有点的单调不降子序列个数(类似于进出站)(相当于以x为下标,以y值为数值求lis个数)
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=30010;
struct aaa{
int x,y;
bool operator <(const aaa e) const{
if(x==e.x) return y>e.y;
return x<e.x;
}
}a[N];
int b[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,x,y,len=0;
cin>>n;
per(i,1,n)
{
scanf("%d%d",&x,&y);
a[i].x=(x+y),a[i].y=y-x;
}
sort(a+1,a+n+1);
b[++len]=a[1].y;
per(i,2,n)
{
if(a[i].y>b[len]) b[++len]=a[i].y;
else b[lower_bound(b+1,b+len+1,a[i].y)-b]=a[i].y;
}
printf("%d\n",len);
return 0;
}
T2:Chocolate
来源:POI2003 BZOJ2430
问题:
分析:
贪心,横竖一起排序,从大到小进行切割(越早切割,需要乘的系数越少),并记录横竖各被切过几次,每次乘上切割的次数
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=1e4+10;
int x[N],y[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n=0,m=0,tx=1,ty=1,s,t;
long long ans=0;
cin>>n>>m;
--n,--m;
per(i,1,n) scanf("%d",x+i);
per(i,1,m) scanf("%d",y+i);
sort(x+1,x+n+1),sort(y+1,y+m+1);
s=n,t=m;
per(i,1,n+m)
{
if(x[s]>y[t])//判断横切还是竖切
{
ty++;
ans+=x[s--]*tx;
}
else
{
tx++;
ans+=y[t--]*ty;
}
}
printf("%lld\n",ans);
return 0;
}
T3:Trinomial
来源:POI2003
问题:
求 \((x^2+x+1)^n\)的第 \(m\) 项系数。
分析:
原式=\(((x-1)^2+3x)^n\),因为3x系数为3,模数也为3,所以只需要求出\((x-1)^{2n}\)的的系数即可,可以推出系数满足杨慧三角(卢卡斯定理)
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int d=3;
int a[3],inv[3];
int C(int n,int m)
{
if(m>n) return 0;
return((a[n]*inv[a[m]])%d*inv[a[n-m]]%d);
}
int lucas(int n,int m)
{
if (!m) return 1;
return C(n%d,m%d)*lucas(n / d, m / d) % d;
}
signed main()
{
int t,n,m;
a[0]=a[1]=inv[0]=inv[1]=1;
a[2]=inv[2]= 2;
cin>>t;
while (t--)
{
scanf("%lld%lld",&n,&m);
if((2*n-m)&1) printf("%lld\n",(lucas(2*n,m)*(-1)+d)%d);
else printf("%lld\n",(lucas(2*n,m)+d)%d);
}
return 0;
}
T4:Gra-Game
来源:POI2004 bzoj2066
问题:
博弈论,见博弈论博客最后一题
T5:Goldmine
来源:POI2001
问题:
分析:
将每一个点扩展成一个s*w的矩形,扫面线求最多重叠的矩形个数,即为答案(类似 窗口的星星)
代码
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=15010;
struct aaa{
int l,r,h,mk;
bool operator <(const aaa e)const{
if(h==e.h) return mk>e.mk;
return h<e.h;
}
}a[N<<1];
struct bbb{
int l,r,sum,add;
}tr[30000*4];
int x[N<<1];
map<int,int>mp;
void build(int u,int l,int r)
{
tr[u].l=l,tr[u].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void pu(int u){ tr[u].sum=max(tr[u<<1].sum,tr[u<<1|1].sum);}
void pd(int u)
{
if(!tr[u].add) return;
tr[u<<1].sum+=tr[u].add,tr[u<<1].add+=tr[u].add;
tr[u<<1|1].sum+=tr[u].add,tr[u<<1|1].add+=tr[u].add;
tr[u].add=0;
}
void addd(int u,int l,int r,int v)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].sum+=v,tr[u].add+=v;
return;
}
pd(u);
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid) addd(u<<1,l,r,v);
if(r>mid) addd(u<<1|1,l,r,v);
pu(u);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int xx,y,s,w,n;
cin>>s>>w>>n;
per(i,1,n)
{
scanf("%d%d",&xx,&y);
x[i*2-1]=xx,x[i*2]=xx+s;
a[i*2-1]=(aaa){xx,xx+s,y,1};
a[i*2]=(aaa){xx,xx+s,y+w,-1};
}
n<<=1;
sort(a+1,a+n+1),sort(x+1,x+n+1);
int tot=unique(x+1,x+n+1)-x-1;
per(i,1,tot) mp[x[i]]=i;
build(1,1,tot);
int ans=0;
per(i,1,n)
{
addd(1,mp[a[i].l],mp[a[i].r],a[i].mk);
ans=max(ans,tr[1].sum);
}
printf("%d\n",ans);
return 0;
}
T6:PTA-Little Bird
来源:POI2014
问题:
分析:
每只鸟只能向后飞到\(k_i\)范围内的树上,反过来,每个状态只能由它的前\(k_i\)个状态转移得到。
考虑使用单调队列,维护当前点前面的\(k_i\)个点所需的最小体力值进行转移(若两个点体力耗费相同,取较高的一个点进行转移)
代码:
#include <bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i(a);i<=b;++i)
const int N=1e6+10;
int d[N],q[N],f[N];
int main()
{
int n,m,k,t,h;
cin>>n;
per(i,1,n) scanf("%d",d+i);
cin>>m;
while(m--)
{
cin>>k;
h=t=1;
q[1]=1;
per(i,2,n)
{
while(h<=t&&i-q[h]>k) ++h;
if(d[q[h]]>d[i]) f[i]=f[q[h]];
else f[i]=f[q[h]]+1;
while(h<=t&&(f[q[t]]>f[i]||(f[q[t]]==f[i]&&d[q[t]]<=d[i]))) --t;
q[++t]=i;
}
printf("%d\n",f[n]);
}
return 0;
}
T7:FES-Festival
来源:POI2012
问题:
分析:
差分约束,m1的边,add(a,b,1),add(b,a,-1);m2的边,add(a,b,0)
对每个强连通分量跑最长路径。因为数值的个数都要+1,所以最后的答案为 最长路径和 + 强连通分量个数。
代码:
#include <bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i(a);i<=b;++i)
const int N=610,M=2e5+10;
int d[N][N],to[M],v[M],nx[M],mx[N],hd[N],id,dfn[N],low[N],bl[N],num,sum;
stack<int>s;
bitset<N>ck;
void addd(int x,int y,int z){ to[++id]=y,v[id]=z,nx[id]=hd[x],hd[x]=id;}
void tarjan(int x)
{
s.emplace(x);
ck[x]=1;
dfn[x]=low[x]=++num;
for(int i=hd[x];i;i=nx[i])
{
if(!dfn[to[i]])
{
tarjan(to[i]);
low[x]=min(low[x],low[to[i]]);
}
else if(ck[to[i]]) low[x]=min(low[x],low[to[i]]);
}
if(dfn[x]==low[x])
{
int t;
++sum;
while(!s.empty())
{
t=s.top(),s.pop();
ck[t]=0;
bl[t]=sum;
if(x==t) break;
}
}
}
int main()
{
int x,y,m1,m2,n;
cin>>n>>m1>>m2;
memset(d,0x3f,sizeof(d));
per(i,1,n) d[i][i]=0;
per(i,1,m1)
{
scanf("%d%d",&x,&y);
addd(x,y,-1),addd(y,x,1);
d[x][y]=min(d[x][y],-1),d[y][x]=min(d[y][x],1);
}
per(i,1,m2)
{
scanf("%d%d",&x,&y);
addd(x,y,0);
d[x][y]=min(d[x][y],0);
}
per(i,1,n) if(!dfn[i]) tarjan(i);
per(k,1,n)
{
per(i,1,n)
{
if(bl[i]!=bl[k]) continue;
per(j,1,n)
{
if(bl[i]!=bl[j]) continue;
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
per(i,1,n) if(d[i][i])
{
puts("NIE");
return 0;
}
per(i,1,n)
{
per(j,1,n)
{
if(bl[i]!=bl[j]) continue;
mx[bl[i]]=max(mx[bl[i]],d[i][j]);
}
}
int ans=0;
per(i,1,sum) ans+=mx[i];
printf("%d\n",ans+sum);
return 0;
}
T8:DYN-Dynamite
来源:POI2011
问题:
分析:
二分+树形dp
\(f[x]表示离x节点最远的未被覆盖的点\)
\(g[x]表示离x节点最近的已点燃的点\)
二分枚举所需最少时间
针对每一个点x:
若\(f[x]+g[x]\leq t\) 说明被点燃的点无论和最远未被覆盖的点在不在同一子树上,都能在t内将其点燃,所以x节点不用被点燃
如果x需要被点燃,且\(g[x]>t\) 则无法按时点燃,需要求助父亲节点
若\(f[x]=t\) 点燃这个节点
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=300010;
int hd[N],nx[N*2],to[N*2],id,g[N],f[N];
int t,s,n;
bool a[N];
void addd(int x,int y){ to[++id]=y,nx[id]=hd[x],hd[x]=id;}
void dfs(int x,int fa)
{
g[x]=2e9,f[x]=-2e9;
for(int i=hd[x];i;i=nx[i])
{
if(to[i]==fa)continue;
dfs(to[i],x);
f[x]=max(f[x],f[to[i]]+1);
g[x]=min(g[x],g[to[i]]+1);
if(f[x]+g[x]<=t) f[x]=-1e9;
}
if(a[x]&&g[x]>t) f[x]=max(f[x],0);
if(f[x]==t) s++,f[x]=-1e9,g[x]=0;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int m,x,y,sum=0;
cin>>n>>m;
per(i,1,n)
{
scanf("%d",&x),a[i]=x;
if(x) ++sum;
}
per(i,1,n-1) scanf("%d%d",&x,&y),addd(x,y),addd(y,x);
int l=0,r=n+1;
if(sum<=m)
{
puts("0");
return 0;
}
while(l+1<r)
{
t=(l+r)>>1,s=0;
dfs(1,0);
if(f[1]>=0) s++;
if(s<=m) r=t;
else l=t;
}
printf("%d\n",r);
return 0;
}
T9:PLA-Postering
来源:POI2008
问题:
N个矩形,排成一排。现在希望用尽量少的矩形海报Cover住它们.
分析:
最多需要n个海报覆盖所有矩形,当且仅当两个矩形等高,且中间的矩形都比他俩高的时候,这两个矩形可以用一个矩形覆盖。
用单调栈来维护,当入队的元素等于队尾的元素的时候,海报数-1
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=5e5+10;
int a[N],s[N];
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,ans,l=0;
cin>>n;
ans=n;
per(i,1,n) scanf("%d%d",a+i,a+i);
per(i,1,n)
{
while(s[l]>a[i]) --l;
if(a[i]==s[l]) --ans;
else s[++l]=a[i];
}
printf("%d\n",ans);
return 0;
}
T10:Viruses
来源:POI2000 BZOJ2938
问题:
分析:
AC自动机,找到一段无限代码,不包含病毒串
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=3e4+10;
int tr[N][2],nx[N];
bool c[N],ck[N],vis[N];
queue<int>q;
void sol()
{
int x,p,to;
q.push(0);
nx[0]=-1;
while(!q.empty())
{
x=q.front(),q.pop();
per(i,0,1)
{
to=tr[x][i];
if(!to)
{
tr[x][i]=tr[nx[x]][i];
continue;
}
q.push(to);
p=nx[x];
while(p!=-1&&!tr[p][i]) p=nx[p];
if(p==-1) nx[to]=0;
else nx[to]=tr[p][i],c[to]|=c[tr[p][i]];
}
}
}
bool dfs(int x)
{
int to;
ck[x]=1;
per(i,0,1)
{
to=tr[x][i];
if(ck[to]) return 1;
if(c[to]||vis[to]) continue;
vis[to]=1;
if(dfs(to)) return 1;
}
ck[x]=0;
return 0;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,x,l,id=0,p;
char s[N];
cin>>n;
per(i,1,n)
{
scanf("%s",s+1);
p=0,l=strlen(s+1);
per(j,1,l)
{
x=s[j]-'0';
if(!tr[p][x]) tr[p][x]=++id;
p=tr[p][x];
}
c[p]=1;
}
sol();
if(dfs(0)) puts("TAK");
else puts("NIE");
return 0;
}
B
T1:ban- Cash Dispenser
来源:POI2005 bzoj1526
问题:
分析:
因为只有四位,枚举每种情况并验证(\(10^4\)种)
\(f[x][k]\)表示第x个字符后面,第一个k出现的位置
每输入一个字符串,都检验1e4是否可行,不行就删去(链表储存)
代码:
#include <bits/stdc++.h>
using namespace std;
#define per(i,a,b) for(int i(a);i<=b;++i)
const int N=1e4+10;
int nx[N],ls[N],m,f[N][10],pos[15],ans=10000;
char s[N];
bool ck(int x)
{
int nw=1;
--x;
per(i,0,3)
{
nw=f[nw][x%10];
if(!nw) return 0;
x/=10;
}
return 1;
}
void sol()
{
int x;
memset(pos,0,sizeof(pos));
per(i,1,m)
{
x=s[i]-'0';
per(j,pos[x]+1,i) f[j][x]=i;
pos[x]=i;
}
per(i,0,9)
{
per(j,pos[i]+1,m) f[j][i]=0;
}
for(int i=nx[0];i<=10000;i=nx[i]) if(!ck(i)) ls[nx[i]]=ls[i],nx[ls[i]]=nx[i],ans--;
}
int main()
{
int n;
cin>>n;
per(i,1,10000) ls[i]=i-1,nx[i]=i+1;
nx[0]=1,ls[10001]=10000;
per(i,1,n)
{
scanf("%d%s",&m,s+1);
sol();
}
printf("%d\n",ans);
return 0;
}
T2: sam-Toy Cars
来源:POI2005 BZOJ1528
问题:
分析:
采取贪心的想法,如果当前地上的车需要选一辆被放回架子,应考虑这个玩具车下次出现的时间,将下次出现时间最晚的放回架子。
用堆维护队列里每个元素下一次最晚出现的时间,若需要拿走一个玩具,将nx最大的出队
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=1e5+10,M=5e5+10,inf=0x3f3f3f3f;
int ls[N],nx[M],p[M];
priority_queue<pair<int,int>>q;
bitset<M>ck;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,m,k,ans=0,sz=0;
cin>>n>>k>>m;
per(i,1,m) scanf("%d",p+i),nx[ls[p[i]]]=i,ls[p[i]]=i;
per(i,1,n) nx[ls[i]]=inf;
per(i,1,m)
{
if(ck[p[i]])
{
q.emplace(nx[i],p[i]);
continue;
}
++ans,++sz;
if(sz>k) --sz,ck[q.top().second]=0,q.pop();
ck[p[i]]=1,q.emplace(nx[i],p[i]);
}
printf("%d\n",ans);
return 0;
}
T3:ska -Piggy banks
来源:POI2005 bzoj1529
问题:
分析:
并查集(基环森林)
n个点,n条边;
归纳可证:只要联通,就一定存在一种方式,敲开一个就能打开所有联通的
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=1000010;
int fa[N];
int getfa(int x){ return fa[x]==x?x:fa[x]=getfa(fa[x]);}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,x,y,ans=0;
cin>>n;
per(i,1,n) fa[i]=i;
per(i,1,n)
{
scanf("%d",&x);
x=getfa(x),y=getfa(i);
if(x!=y) fa[x]=y;
}
per(i,1,n) if(fa[i]==i)++ans;
printf("%d\n",ans);
return 0;
}
T4: LOT-A Journey to Mars
来源:POI2005 bzoj1533
问题:
分析:
断环为链,分别按顺时针,逆时针求前缀和
单调队列维护每个长度为n的区间内的前缀和最小值(最少的油料剩余量)
如果开始的那个点的油料比此最小值低,说明能够走完一圈,否则不能
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=2e6+10;
int a[N],b[N],q[N];
long long v[N],ans[N];
bitset<N>fg;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,h,t;
cin>>n;
per(i,1,n)
{
scanf("%d%d",a+i,b+i);
a[i+n]=a[i],b[i+n]=b[i];//断环为链
}
per(i,1,n*2) v[i]=v[i-1]+a[i]-b[i]; //顺时针缀和
h=1,t=0;
per(i,1,n*2)
{
while(h<=t&&q[h]<i-n+1) ++h;
while(h<=t&&v[i]<=v[q[t]]) --t;
q[++t]=i;
if(i>=n) ans[i-n+1]=v[q[h]];
}
per(i,1,n) if(ans[i]>=v[i-1]) fg[i]=1;
int tmp=b[n];
for(int i=n;i>1;--i) b[i]=b[i-1];//逆时针跑
b[1]=tmp;
per(i,1,n/2)
{
swap(a[i],a[n-i+1]);
swap(b[i],b[n-i+1]);
}
per(i,1,n) a[i+n]=a[i],b[i+n]=b[i];
per(i,1,n*2) v[i]=v[i-1]+a[i]-b[i];
h=1,t=0;
per(i,1,n*2)
{
while(h<=t&&q[h]<i-n+1) ++h;
while(h<=t&&v[i]<=v[q[t]]) --t;
q[++t]=i;
if(i>=n) ans[i-n+1]=v[q[h]];
}
per(i,1,n) if(ans[i]>=v[i-1]) fg[n-i+1]=1;
per(i,1,n)
{
if(fg[i]) puts("TAK");
else puts("NIE");
}
return 0;
}
T5:BAN-Bank Notes
来源: POI2005 bzoj1531
问题:
分析:
背包问题,单调队列优化多重背包
dp[m] = max(dp[m], dp[m-v] + w, dp[m-2*v] + 2*w, dp[m-3*v] + 3*w, ...)
接下来,我们把 dp[0] --> dp[m] 写成下面这种形式
dp[0], dp[v], dp[2*v], dp[3*v], ... , dp[k*v]
dp[1], dp[v+1], dp[2*v+1], dp[3*v+1], ... , dp[k*v+1]
dp[2], dp[v+2], dp[2*v+2], dp[3*v+2], ... , dp[k*v+2]
...
dp[j], dp[v+j], dp[2*v+j], dp[3*v+j], ... , dp[k*v+j]
将体积按对当前v取余所得余数分类
所以,我们可以得到
dp[j] = dp[j]
dp[j+v] = max(dp[j] + w, dp[j+v])
dp[j+2v] = max(dp[j] + 2w, dp[j+v] + w, dp[j+2v])
dp[j+3v] = max(dp[j] + 3w, dp[j+v] + 2w, dp[j+2v] + w, dp[j+3v])
...
但是,这个队列中前面的数,每次都会增加一个 w ,所以我们需要做一些转换
dp[j] = dp[j]
dp[j+v] = max(dp[j], dp[j+v] - w) + w
dp[j+2v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w) + 2w
dp[j+3v] = max(dp[j], dp[j+v] - w, dp[j+2v] - 2w, dp[j+3v] - 3w) + 3w
...
这样,每次入队的值是 dp[j+k*v] - k*w
单调队列求最大值
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=210,M=2e4+10;
int v[N],c[N],q[M],dp[M],f[M];
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
int n,m,h,t;
cin>>n;
per(i,1,n) scanf("%d",v+i);
per(i,1,n) scanf("%d",c+i);
cin>>m;
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
per(i,1,n)
{
memcpy(f,dp,sizeof(dp));//滚动数组
per(j,0,v[i]-1)
{
h=1,t=0;
for(int k=j;k<=m;k+=v[i])
{
while(h<=t&&(k-q[h])>c[i]*v[i]) ++h;
while(h<=t&&(f[q[t]]-(q[t]-j)/v[i])>=(f[k]-(k-j)/v[i])) --t;
q[++t]=k;
dp[k]=min(dp[k],f[q[h]]+(k-q[h])/v[i]);
}
}
}
printf("%d\n",dp[m]);
return 0;
}
T6:SUM-Fibonacci Sums
来源:POI2005 bzoj1534
问题:
分析:
用斐波那契数列来表示整数(Zeckendorf Arithmetic)
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=1e6;
int a[N+10];
queue<int>q;
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int x,y,e;
cin>>x;
per(i,1,x) scanf("%d",a+i);
cin>>y;
per(i,1,y) scanf("%d",&e),a[i]+=e;
for(int i=N;i>=1;--i)
{
if(a[i]==0&&a[i-1]==2&&a[i-2]==0)
a[i]=1,a[i-1]=0,a[i-2]=0,a[i-3]++;
else if(a[i]==0&&a[i-1]==3&&a[i-2]==0)
a[i]=1,a[i-1]=1,a[i-2]=0,a[i-3]++;
else if(a[i]==0&&a[i-1]==2&&a[i-2]==1)
a[i]=1,a[i-1]=1,a[i-2]=0;
else if(a[i]==0&&a[i-1]==1&&a[i-2]==2)
a[i]=1,a[i-1]=0,a[i-2]=1;
}
for(int i=N;i>=1;--i)
if(a[i]==0&&a[i-1]==1&&a[i-2]==1)
a[i]=1,a[i-1]=0,a[i-2]=0;
if(a[0]==1) a[1]=1;
per(i,3,N)
if(a[i]==0&&a[i-1]==1&&a[i-2]==1)
a[i]=1,a[i-1]=0,a[i-2]=0;
int ans;
for(ans=N;ans>=1;ans--)
{
if(a[ans])
{
printf("%d ",ans);
break;
}
}
per(i,1,ans) printf("%d ",a[i]);
return 0;
}
T7: Sza-Template
来源:POI2005 bzoj1535
问题:
分析:
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
struct Edge{
int next,num;
}e[N*3];
int n,mx,ans,cnt,pre[N],nxt[N],used[N],fail[N];
char st[N];
void add(int x, int y)
{
e[++cnt]=(Edge) {e[x].next, y};
e[x].next=cnt;
}
void kmp()
{
fail[1] = 0;
for (int i = 2; i <= n; i++)
{
int j = fail[i - 1];
while (j && st[j + 1] != st[i]) j = fail[j];
if (st[j + 1] == st[i]) j++; fail[i] = j;
}
}
void del(int x)
{
pre[nxt[x]] = pre[x], nxt[pre[x]] = nxt[x];
mx = max(mx, nxt[x] - pre[x]);
for (int p = e[x].next; p; p = e[p].next)
del(e[p].num);
}
void dfs(int x)
{
if (mx <= x) return ans = x, void();
int nxt = 0;
for (int p = e[x].next; p; p = e[p].next) {
int k = e[p].num;
if (used[k]) nxt = k;
else del(k);
}
dfs(nxt);
}
int main()
{
scanf("%s", st + 1);
n = strlen(st + 1);
cnt = n;
kmp();
for (int i = 1; i <= n; i++)
add(fail[i], i), pre[i] = i - 1, nxt[i] = i + 1;
nxt[n] = 0, mx = 1;
for (int i = n; i; i = fail[i]) used[i] = 1;
dfs(0); cout << ans << "\n";
return 0;
}
T8:DWU-Double-row
来源:POI2005 bzoj1539
问题:
分析:
类似差分约束,图论。
若两个相同的数在同一行里,将他俩用1边相连
若两个相同的数在不同行中,讲他俩用0边相连
答案为每一个连通块里min(1,0)个数的和
代码:
#include <bits/stdc++.h>
#define per(i,a,b) for(int i(a);i<=b;++i)
using namespace std;
const int N=5e4+10;
int hd[N],nx[N*5],to[N*5],v[N*5],id;
int a[N],b[N],ans;
map<int,int>ca,cb;
bitset<N>ck,c;
void addd(int x,int y,int z){to[++id]=y,v[id]=z,nx[id]=hd[x],hd[x]=id;}
queue<int>q;
void bfs(int x)
{
q.push(x);
c[x]=1,ck[x]=1;
int tot=1,num=0;
while(!q.empty())
{
x=q.front(),q.pop();
for(int i=hd[x];i;i=nx[i])
{
if(ck[to[i]]) continue;
++tot;
q.push(to[i]);
ck[to[i]]=1;
c[to[i]]=c[x]^v[i];
if(!c[to[i]]) ++num;
}
}
ans+=min(num,tot-num);
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n;
cin>>n;
per(i,1,n)
{
scanf("%d",a+i);
if(ca[a[i]]) addd(ca[a[i]],i,1),addd(i,ca[a[i]],1);
else ca[a[i]]=i;
}
per(i,1,n)
{
scanf("%d",b+i);
if(ca[b[i]]) addd(ca[b[i]],i,0),addd(i,ca[b[i]],0);
else if(cb[b[i]]) addd(cb[b[i]],i,1),addd(i,cb[b[i]],1);
else cb[b[i]]=i;
}
per(i,1,n) if(!ck[i]) bfs(i);
printf("%d\n",ans);
return 0;
}
T9:Aut-The Bus
来源:POI2005 bzoj1537
问题:
分析:
二维偏序(CDQ分治)
代码:
// By SiriusRen
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100050;
#define int long long
int n, m, k, recy[N], f[N];
struct Node {
int x, y, p;
} node[N];
bool cmp(Node a, Node b) {
if (a.x != b.x)
return a.x < b.x;
return a.y < b.y;
}
struct Temp {
int op, k, id;
Temp() {}
Temp(int x, int y, int z) { op = x, k = y, id = z; }
} temp[N];
bool Cmp(Temp a, Temp b) {
if (a.k != b.k)
return a.k < b.k;
return a.op > b.op;
}
void cdq(int l, int r) {
if (l >= r) {
f[l] = max(node[l].p, f[l]);
return;
}
int mid = (l + r) >> 1, top = 0, maxx = 0;
cdq(l, mid);
for (int i = l; i <= mid; i++) temp[++top] = Temp(1, node[i].y, i);
for (int i = mid + 1; i <= r; i++) temp[++top] = Temp(0, node[i].y, i);
sort(temp + 1, temp + 1 + top, Cmp);
for (int i = 1; i <= top; i++) {
if (temp[i].op)
maxx = max(f[temp[i].id], maxx);
else
f[temp[i].id] = max(f[temp[i].id], maxx + node[temp[i].id].p);
}
cdq(mid + 1, r);
}
signed main() {
scanf("%lld%lld%lld", &n, &m, &k);
for (int i = 1; i <= k; i++)
scanf("%lld%lld%lld", &node[i].x, &node[i].y, &node[i].p), recy[i] = node[i].y;
sort(node + 1, node + 1 + k, cmp);
cdq(1, k);
for (int i = 1; i <= k; i++) f[k] = max(f[k], f[i]);
printf("%lld\n", f[k]);
}
T10:PRO-Professor Szu
来源:POI2006 bzoj1512
问题:
分析:
鸽了
代码:
puts("-1")