Plan metra:
找出 1 1 1- n n n的路径后随便构造就行了,一开始我想找 d 1 + d n d_1+d_n d1+dn相等的点作为路径上的点,其实找 d 1 + d n d_1+d_n d1+dn最小的才是正确的,然后还要特判 1 1 1和 n n n直接相连的情况,这时可以用所有点 ∣ d 1 − d n ∣ |d_1-d_n| ∣d1−dn∣相同来判断,但是当 ∣ d 1 − d n ∣ |d_1-d_n| ∣d1−dn∣都为 0 0 0时, 1 1 1和 n n n不是直接相连的,要特判。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=500010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,mn=inf,lb=0,len=0;
struct Node{int d1,dn,x,id;}a[Maxn],b[Maxn];
bool cmp(Node a,Node b){return a.d1<b.d1;}
struct Edge{int x,y,d;}e[Maxn];
int mark[1000010];
int main()
{
n=read();bool flag=true;
if(n==2)return printf("TAK\n1 2 1"),0;
for(int i=2;i<n;i++)a[i].d1=read(),a[i].id=i;
for(int i=2;i<n;i++)
{
a[i].dn=read();
a[i].x=a[i].d1-a[i].dn;
mn=min(mn,a[i].d1+a[i].dn);
if(i>2&&abs(a[i].x)!=abs(a[i-1].x))flag=false;
}
if(flag)
{
int dis=abs(a[2].x);
if(!dis)
{
if(n==3)
{
puts("TAK");
printf("%d %d %d\n",1,2,a[2].d1);
printf("%d %d %d\n",3,2,a[2].d1);
return 0;
}
sort(a+2,a+n,cmp);
if(a[2].d1==a[3].d1)return puts("NIE"),0;
puts("TAK");
printf("%d %d %d\n",1,a[2].id,a[2].d1);
printf("%d %d %d\n",n,a[2].id,a[2].d1);
for(int i=3;i<n;i++)printf("%d %d %d\n",a[2].id,a[i].id,a[i].d1-a[2].d1);
return 0;
}
puts("TAK");
printf("%d %d %d\n",1,n,dis);
for(int i=2;i<n;i++)
{
if(a[i].x>0)printf("%d %d %d\n",i,n,a[i].d1-dis);
else printf("%d %d %d\n",i,1,a[i].dn-dis);
}
return 0;
}
else
{
memset(mark,-1,sizeof(mark));
int dis=mn;
for(int i=2;i<n;i++)
{
if(a[i].x==dis)e[++len].x=i,e[len].y=n,e[len].d=a[i].d1-dis;
else if(a[i].x==-dis)e[++len].x=i,e[len].y=1,e[len].d=a[i].dn-dis;
else b[++lb]=a[i],b[lb].id=i;
}
sort(b+1,b+1+lb,cmp);
int last=1,lastd=0;
for(int i=1;i<=lb;i++)
{
if(b[i].d1+b[i].dn==dis)
{
e[++len].x=last,e[len].y=b[i].id,e[len].d=b[i].d1-lastd;
if(e[len].d<=0)return puts("NIE"),0;
last=b[i].id,lastd=b[i].d1;mark[b[i].d1]=b[i].id;
}
else
{
int d=b[i].d1+b[i].dn-dis;
if(d&1)return puts("NIE"),0;
d>>=1;
if(b[i].d1<=d||mark[b[i].d1-d]==-1)return puts("NIE"),0;
e[++len].x=mark[b[i].d1-d],e[len].y=b[i].id,e[len].d=d;
}
}
e[++len].x=last,e[len].y=n,e[len].d=dis-lastd;
if(len!=n-1||e[len].d<=0)return puts("NIE"),0;
puts("TAK");
for(int i=1;i<=len;i++)printf("%d %d %d\n",e[i].x,e[i].y,e[i].d);
}
}
Powódź:
这个题的话抓住如果某个格子的水位高于墙,那么它周围的一些格子的水位必须跟它一样这个性质做,按照墙的高度排序,每次把两个块合并,维护合法的答案、高度即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=500010;
const int inf=2147483647;
const int mod=1000000007;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,H,cnt=0;
int P(int x,int y){return m*(x-1)+y;}
struct Node
{
int v,type,x,y;
}a[Maxn<<1];
bool cmp(Node a,Node b){return a.v<b.v;}
int Pow(int x,int y)
{
if(!y)return 1;
if(y==1)return x;
int t=Pow(x,y>>1),re=(LL)t*t%mod;
if(y&1)re=(LL)re*x%mod;
return re;
}
int fa[Maxn],mx[Maxn],ans[Maxn];
int findfa(int x){return(fa[x]==x?x:fa[x]=findfa(fa[x]));}
void merge(int x,int y,int h)
{
int fx=findfa(x),fy=findfa(y);
if(fx!=fy)
{
ans[fy]=(LL)(ans[fy]+h-mx[fy])*(ans[fx]+h-mx[fx])%mod;
mx[fy]=h;
fa[fx]=fy;
}
}
int main()
{
n=read(),m=read(),H=read();
for(int i=1;i<=n;i++)
for(int j=1;j<m;j++)
a[++cnt].v=read(),a[cnt].x=i,a[cnt].y=j,a[cnt].type=0;
for(int i=1;i<n;i++)
for(int j=1;j<=m;j++)
a[++cnt].v=read(),a[cnt].x=i,a[cnt].y=j,a[cnt].type=1;
sort(a+1,a+1+cnt,cmp);
for(int i=1;i<=n*m;i++)fa[i]=i,mx[i]=-1,ans[i]=0;
for(int i=1;i<=cnt;i++)
if(a[i].type==0)merge(P(a[i].x,a[i].y),P(a[i].x,a[i].y+1),a[i].v);
else merge(P(a[i].x,a[i].y),P(a[i].x+1,a[i].y),a[i].v);
printf("%d",(ans[findfa(1)]+H-mx[findfa(1)])%mod);
}
Prawnicy:
这个题做法非常显然啊,考虑答案一定是以某段开头的,那么就枚举是第 i i i段。那么也就是要找 k k k段满足 l < = l i l<=l_i l<=li,然后令这 k k k个中的 r r r最小值尽量大,这个可以用数据结构实现,也可以用一个 s i z e size size为 k k k的堆来实现。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=1000010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,k,L,R;
struct Line{int l,r,id;}a[Maxn];
bool cmp(Line a,Line b)
{
if(a.l!=b.l)return a.l<b.l;
return a.r>b.r;
}
priority_queue<int>q;int sz=0;
int main()
{
n=read(),k=read();
for(int i=1;i<=n;i++)a[i].l=read(),a[i].r=read(),a[i].id=i;
sort(a+1,a+1+n,cmp);
int ans=0;
for(int i=1;i<=n;i++)
{
int t;
if(sz<k)q.push(-a[i].r),sz++;
else
{
t=-q.top();
if(t<a[i].r)q.pop(),q.push(-a[i].r);
}
if(sz==k)
{
t=-q.top();
int tmp=t-a[i].l;
if(tmp>ans)ans=tmp,L=a[i].l,R=t;
}
}
printf("%d\n",ans);int K=0;
for(int i=1;i<=n;i++)
if(a[i].l<=L&&a[i].r>=R)
{
K++;
printf("%d ",a[i].id);
if(K==k)break;
}
}
Różnorodność:
这个题不会啊。考虑暴力,每次移动一个 k × k k\times k k×k的正方形,然后删除 k k k个位置的贡献,再加入 k k k个位置的贡献,这样是 O ( n m k ) O(nmk) O(nmk)的。考虑每个位置,被反复地删除和插入,非常耗时。考虑怎么使每个格子的贡献只被考虑一次,我们可以每次计算一行( m − k + 1 m-k+1 m−k+1个)正方形的答案,那么每次就是删去一行数的贡献和加入一行数的贡献,对于每个数,找到它的前驱和后继(以它所在的列为关键字),然后就可以知道它影响的是哪一段区间的答案,差分就好了,找前驱后继用线段树实现,复杂度 O ( n m l o g m ) O(nmlogm) O(nmlogm),但是没有过。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int Maxn=3010;
const int Maxv=100010;
const int inf=2147483647;
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int n,m,k,a[Maxn][Maxn],cnt[Maxv],d[Maxn];
int root[Maxv],lc[Maxv<<8],rc[Maxv<<8],c[Maxv<<8],tot=0;
void up(int x){c[x]=c[lc[x]]+c[rc[x]];}
void add(int &x,int l,int r,int p,int v)
{
if(!x)x=++tot;
if(l==r){c[x]+=v;return;}
int mid=l+r>>1;
if(p<=mid)add(lc[x],l,mid,p,v);
else add(rc[x],mid+1,r,p,v);
up(x);
}
int q1(int x,int l,int r,int p)
{
if(!x||!c[x])return -1;
if(l==r)return l;
int mid=l+r>>1;
if(p<=mid)return q1(lc[x],l,mid,p);
int t=q1(rc[x],mid+1,r,p);
if(t!=-1)return t;
return q1(lc[x],l,mid,p);
}
int q2(int x,int l,int r,int p)
{
if(!x||!c[x])return -1;
if(l==r)return l;
int mid=l+r>>1;
if(p>mid)return q2(rc[x],mid+1,r,p);
int t=q2(lc[x],l,mid,p);
if(t!=-1)return t;
return q2(rc[x],mid+1,r,p);
}
void cmax(int &x,int y){x=max(x,y);}
void cmin(int &x,int y){x=min(x,y);}
int ans1=0;LL ans2=0;
int Add(int l,int r,int o)
{
d[l]+=o;
if(r+1<=m-k+1)d[r+1]-=o;
}
int main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=read();
for(int i=1;i<k;i++)
for(int j=1;j<=k;j++)
{
if(!cnt[a[i][j]])d[1]++;
cnt[a[i][j]]++;
}
for(int j=k+1;j<=m;j++)
for(int i=1;i<k;i++)
{
cnt[a[i][j-k]]--;
if(!cnt[a[i][j-k]])d[j-k+1]--;
if(!cnt[a[i][j]])d[j-k+1]++;
cnt[a[i][j]]++;
}
for(int i=1;i<k;i++)
for(int j=1;j<=m;j++)
add(root[a[i][j]],1,m,j,1);
for(int i=k;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int L=q1(root[a[i][j]],1,m,j),R=q2(root[a[i][j]],1,m,j);
int ll=max(1,j-k+1),rr=min(m-k+1,j);
if(L!=-1)cmax(ll,L+1);
if(R!=-1)cmin(rr,R-k);
if(ll<=rr)Add(ll,rr,1);
add(root[a[i][j]],1,m,j,1);
}
int t=0;
for(int j=1;j<=m-k+1;j++)
{
t+=d[j];
cmax(ans1,t);ans2+=t;
}
for(int j=1;j<=m;j++)
{
add(root[a[i-k+1][j]],1,m,j,-1);
int L=q1(root[a[i-k+1][j]],1,m,j),R=q2(root[a[i-k+1][j]],1,m,j);
int ll=max(1,j-k+1),rr=min(m-k+1,j);
if(L!=-1)cmax(ll,L+1);
if(R!=-1)cmin(rr,R-k);
if(ll<=rr)Add(ll,rr,-1);
}
}
printf("%d %lld",ans1,ans2);
}