T1:用tarjan求一遍点双,然后判断每个点双内的点数是否等于边数,是就把这些边加入答案。
具体求点双的参见“tarjan”有关算法总结。
T2:这是一道大水题,但是我却没有想出来。
首先将m-n*a[1],然后发现如果在第i个位置加A,那么对最终答案的影响就是(n-i+1)*A,如果减B就是-B*(n-i+1)。现在的目标就是求出那些位置加A,那些位置减B。
设加A位置的系数和为x,减B位置的系数和为y,那么:
1、Ax-By=m'
2、x+y=n*(n-1)/2
解出方程,在暴力拆分x或y即可。
总结:还是要多列性质和方向。
T3:首先暴力连边+拓扑序求解的方法很容易想到。
接着我们发现每次连边都是若干个点向若干个区间连,这时可以用线段树来优化连边。
首先对于一次权值大的点连向一个权值小的区间,我们可以新建一个节点,权值大的点连一条1边到这个新点,新点再连一条0边到权值小的区间。注意此时一个区间可以拆分成线段树上的log个区间,这样就保证了复杂度是nlogn的。
最后为了保证连边的合法性,我们对于每个叶节点,要将它的所有祖先向它连一条0边。这样就保证了构出来的拓扑图是正确的。
总结:这种线段树优化连边的方式在一个点连向一个区间是可以使用。而它保证正确性的精髓就是每一个叶子节点的所有祖先都要向它连边。
贴一下代码,方便理解:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 100010
#define MAXM 8000010
#define MAXP 3000010
struct map
{
int x;
int y;
int l;
};
map way[MAXM];
int first[MAXP],nxt[MAXM],num[MAXN],a[MAXP],d[MAXP],cnt[MAXP],fa[MAXP],bz[MAXP],n,ne,m,q;
int x,y,s;
int dg(int l,int r,int t)
{
if(t>ne)ne=t;
if(l==r)num[l]=t;
else
{
fa[t*2]=t;dg(l,(l+r)/2,t*2);
fa[t*2+1]=t;dg((l+r)/2+1,r,t*2+1);
}
}
int link(int l,int r,int t)
{
if(x<=l&&r<=y){m++;way[m].x=ne;way[m].y=t;way[m].l=0;}
else
{
if(l==r)return 0;
if(l<=x&&x<=(l+r)/2||l<=y&&y<=(l+r)/2||x<=l&&(l+r)/2<=y)link(l,(l+r)/2,t*2);
if((l+r)/2+1<=x&&x<=r||(l+r)/2+1<=y&&y<=r||x<=(l+r)/2+1&&r<=y)
link((l+r)/2+1,r,t*2+1);
}
}
int main()
{
//freopen("web.in","r",stdin);
//freopen("web.out","w",stdout);
int i,j,l,r,k,p,v,w,h,t;
scanf("%d %d %d",&n,&s,&q);
dg(1,n,1);
for(i=1;i<=n;i++)
{
j=num[i];
while(fa[j]!=0)
{
m++;way[m].x=fa[j];way[m].y=num[i];way[m].l=0;
j=fa[j];
}
}
while(s>=1)
{
scanf("%d %d",&p,&v);
a[num[p]]=v;bz[num[p]]=1;s--;
}
while(q>=1)
{
scanf("%d %d %d",&l,&r,&k);
ne++;h=l;
while(k>=1)
{
scanf("%d",&w);
m++;way[m].x=num[w];way[m].y=ne;way[m].l=1;
x=h;y=w-1;
if(x<=y)link(1,n,1);
h=w+1;
k--;
}
x=h;y=r;
if(x<=y)link(1,n,1);
q--;
}
for(i=m;i>=1;i--)nxt[i]=first[way[i].x],first[way[i].x]=i;
for(i=1;i<=m;i++)cnt[way[i].y]++;
h=0;t=0;
for(i=1;i<=ne;i++)
if(bz[i]==0)a[i]=1000000000;
for(i=1;i<=ne;i++)
if(cnt[i]==0){t++;d[t]=i;}
while(h<t)
{
h++;
for(i=first[d[h]];i>=1&&i<=m;i=nxt[i])
{
if(bz[way[i].y]==0)
if(a[d[h]]-way[i].l<a[way[i].y])a[way[i].y]=a[d[h]]-way[i].l;
cnt[way[i].y]--;
if(cnt[way[i].y]==0){t++;d[t]=way[i].y;}
}
}
printf("Possible\n");
for(i=1;i<=n;i++)printf("%d ",a[num[i]]);
}