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住它们.

img img
分析:

最多需要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

问题:

分析:

fail树,删点+搜索

代码:
#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")
posted @ 2022-11-07 21:32  f2021yjm  阅读(19)  评论(0编辑  收藏  举报