Solar Panels:
简单题,直接分块对每种数判断是否合法就行了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
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 main()
{
int T=read();
while(T--)
{
int l1=read(),r1=read(),l2=read(),r2=read();
int l=max(l1,l2),r=min(r1,r2);
if(l<=r){printf("%d\n",r);continue;}
if(l1>l2)swap(l1,l2),swap(r1,r2);
int pos,ans=1;
for(int i=l1;i<=r1;i=pos+1)
{
int t1=l2/i,t2=r2/i,nl=min(r1,l2/(l2/i)),nr=min(r1,r2/(r2/i));
pos=min(nl,nr);
if(l2%i==0||t1!=t2)ans=i;
if(l2%pos==0||t1!=t2)ans=pos;
}
if(ans!=1){printf("%d\n",ans);continue;}
int cnt=0;
for(int i=1;i<l1;i=pos+1)
{
cnt++;
int tl1=l1/i,tr1=r1/i,nl1=min(l1-1,l1/(l1/i)),nr1=min(l1-1,r1/(r1/i));
int tl2=l2/i,tr2=r2/i,nl2=min(l1-1,l2/(l2/i)),nr2=min(l1-1,r2/(r2/i));
pos=min(min(nl1,nl2),min(nr1,nr2));
if((l1%i==0||tl1!=tr1)&&(l2%i==0||tl2!=tr2))ans=i;
if((l1%pos==0||tl1!=tr1)&&(l2%pos==0||tl2!=tr2))ans=pos;
}
printf("%d\n",ans);
}
}
Little Bird:
考虑最简单的DP:
f
i
=
f
j
(
D
i
<
D
j
)
f_i=f_j(D_i<D_j)
fi=fj(Di<Dj),
f
i
=
f
j
+
1
(
D
i
>
=
D
j
)
f_i=f_j+1(D_i>=D_j)
fi=fj+1(Di>=Dj)。
看上去像单调队列的样子,但是无法解决括号内的限制。
但是由于后面的式子只会使答案+1,所以可以直接维护一个
f
f
f单调递增的单调队列,这样就可以保证队头的一定是最优的之一。
如果
f
f
f相等就保留
D
D
D大的。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
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 Q,n,k,D[Maxn],q[Maxn],head,tail,f[Maxn];
int main()
{
n=read();
for(int i=1;i<=n;i++)D[i]=read();
Q=read();
while(Q--)
{
k=read();
head=tail=1;
q[1]=1;f[1]=0;
for(int i=2;i<=n;i++)
{
while(head<=tail&&q[head]<i-k)head++;
f[i]=f[q[head]]+(D[q[head]]<=D[i]);
while(head<=tail&&(f[q[tail]]>f[i]||(f[q[tail]]==f[i]&&D[i]>=D[q[tail]])))tail--;
q[++tail]=i;
}
printf("%d\n",f[n]);
}
}
Card:
水题,线段树直接维护区间内两个端点四种选择情况是否可行即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
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,q,v[Maxn][2];
bool c[Maxn<<2][2][2];
void up(int x,int lc,int rc,int p)
{
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
c[x][i][j]=false,
c[x][i][j]|=
(
((v[p][0]<=v[p+1][0])&c[lc][i][0]&c[rc][0][j])|
((v[p][0]<=v[p+1][1])&c[lc][i][0]&c[rc][1][j])|
((v[p][1]<=v[p+1][0])&c[lc][i][1]&c[rc][0][j])|
((v[p][1]<=v[p+1][1])&c[lc][i][1]&c[rc][1][j])
);
}
void w(int x,int l,int r)
{
if(l==r)
{
c[x][0][0]=c[x][1][1]=true;
c[x][0][1]=c[x][1][0]=false;
return;
}
int mid=l+r>>1,lc=(x<<1),rc=((x<<1)|1);
w(lc,l,mid),w(rc,mid+1,r);
up(x,lc,rc,mid);
}
void modify(int x,int l,int r,int p)
{
if(l==r)return;
int mid=l+r>>1,lc=(x<<1),rc=((x<<1)|1);
if(p<=mid)modify(lc,l,mid,p);
else modify(rc,mid+1,r,p);
up(x,lc,rc,mid);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)v[i][0]=read(),v[i][1]=read();
w(1,1,n);
q=read();
while(q--)
{
int x=read(),y=read();
swap(v[x][0],v[y][0]),swap(v[x][1],v[y][1]);
modify(1,1,n,x),modify(1,1,n,y);
if(c[1][0][0]||c[1][0][1]||c[1][1][0]||c[1][1][1])puts("TAK");
else puts("NIE");
}
}
Bricks:
先把最多的颜色找出来,然后把首颜色插到前面的一段空,把尾颜色插到后面的一段空,然后再把剩下的颜色插空。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
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 A[10],lA=0;
void write(int x)
{
if(!x){putchar('0');return;}
lA=0;while(x)A[++lA]=x%10,x/=10;
for(int i=lA;i;i--)putchar(A[i]+48);
}
int n,Start,End,lst[Maxn],nxt[Maxn],col[Maxn],st,ed,tot=0,cnt[Maxn],tcnt[Maxn],ans[Maxn],la=0;
struct C{int x,id;}a[Maxn];
bool cmp(C a,C b)
{
if(a.x!=b.x)return a.x>b.x;
if(a.id!=Start&&a.id!=End&&b.id!=Start&&b.id!=End)return a.id<b.id;
if(a.id==Start&&b.id==End)return true;
if(a.id==End&&b.id==Start)return false;
if(a.id==Start||a.id==End)return false;
return true;
}
void insert(int x,int y,int z)
{
col[++tot]=z;
if(x!=-1)nxt[x]=tot;else st=tot;
if(y!=-1)lst[y]=tot;else ed=tot;
lst[tot]=x,nxt[tot]=y;
}
void work(int pos)
{
for(int i=1;i<=n;i++)
{
if(!a[i].x)continue;
for(int j=1;j<=a[i].x;j++)
{
if(pos==ed)pos=st;
int p=nxt[pos];
insert(pos,nxt[pos],a[i].id);
pos=p;
}
a[i].x=0;
}
}
bool check()
{
if(ans[1]!=Start)return false;
if(ans[la]!=End)return false;
for(int i=1;i<=la;i++)
{
tcnt[ans[i]]++;
if(i>1&&ans[i]==ans[i-1])return false;
}
for(int i=1;i<=n;i++)if(cnt[i]!=tcnt[i])return false;
return true;
}
int main()
{
n=read(),Start=read(),End=read();
bool flag=false;
for(int i=1;i<=n;i++)a[i].x=cnt[i]=read(),a[i].id=i;
if(a[Start].x<a[End].x)swap(Start,End),flag=true;
sort(a+1,a+1+n,cmp);
for(int i=1;i<=a[1].x;i++)insert((i==1)?(-1):i-1,-1,a[1].id);
a[1].x=0;
if(Start==End)
{
if(a[1].id==Start)work(st);
else
{
st=tot+1;
for(int i=1;i<=n;i++)
if(a[i].id==Start)
{
int p=tot,t=a[i].x;a[i].x=0;
for(int j=1;j<t;j++)insert(lst[j],j,Start);
insert(p,-1,Start);
work(t-1);
break;
}
}
}
else
{
if(a[1].id!=Start&&a[1].id!=End)
{
int ppp;
for(int i=1;i<=n;i++)
if(a[i].id==Start)
{
int t=a[i].x;ppp=t,a[i].x=0;
for(int j=1;j<=t;j++)insert(lst[j],j,Start);
break;
}
for(int i=1;i<=n;i++)
if(a[i].id==End)
{
int p=ed,t=a[i].x;a[i].x=0;
for(int j=1;j<=t;j++)insert(p,nxt[p],End),p=lst[p];
break;
}
work(ppp);
}
else
{
int p=tot;
for(int i=1;i<=n;i++)
if(a[i].id==End)
{
int t=a[i].x;a[i].x=0;
for(int j=1;j<=t;j++)insert(p,nxt[p],End),p=lst[p];
break;
}
work(1);
}
}
for(int i=st;i!=-1;i=nxt[i])ans[++la]=col[i];
if(!check())return printf("0"),0;
if(flag)
{
for(int i=1;i<=(la>>1);i++)swap(ans[i],ans[la-i+1]);
}
for(int i=1;i<la;i++)write(ans[i]),putchar(' ');
printf("%d",ans[la]);
}
Salad Bar:
O ( n ) O(n) O(n)预处理出每个位置向左向右最多延伸到哪里,然后用单调栈完成求答案。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
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,nxt[Maxn],pos[Maxn<<1],p[2][Maxn],fir[Maxn<<1],sta[Maxn],top=0;
char s[Maxn];
void work(int o)
{
for(int i=-n;i<=n;i++)pos[i+n]=fir[i+n]=-1;
int t=n,sum=n;
for(int i=1;i<=n;i++)
{
int x=((s[i]=='j')?-1:1);
sum+=x;nxt[i]=-1;
if(pos[sum]==-1)fir[sum]=i;
else nxt[pos[sum]]=i;
pos[sum]=i;
}
sum=n;
for(int i=1;i<=n;i++)
{
int x=((s[i]=='j')?-1:1);
sum+=x;
if(x==1)
{
if(fir[-1+t]==-1)p[o][i]=n;
else p[o][i]=fir[-1+t]-1;
}
fir[sum]=nxt[i];t+=x;
}
}
int main()
{
n=read();
scanf("%s",s+1);
work(0);
for(int i=1;i<=(n>>1);i++)swap(s[i],s[n-i+1]);
work(1);
for(int i=1;i<=(n>>1);i++)swap(p[1][i],p[1][n-i+1]);
for(int i=1;i<=n;i++)if(p[1][i])p[1][i]=n-p[1][i]+1;
int ans=0;
for(int i=n;i;i--)
{
if(s[n-i+1]=='p')
{
while(top&&p[1][i]<p[1][sta[top]])top--;
sta[++top]=i;
}
int l=1,r=top;bool flag=false;
while(l<=r)
{
int mid=l+r>>1;
if(sta[mid]<=p[0][i]){r=mid-1;flag=true;}
else l=mid+1;
}
if(flag)ans=max(ans,sta[r+1]-i+1);
}
printf("%d",ans);
}
Rally:
这题好神……
先给把最长路的起点终点固定,新建源点
S
S
S和汇点
T
T
T,
S
S
S连向所有点,所有点连向
T
T
T。设
f
x
f_x
fx为
S
S
S到
x
x
x的最长路,
g
x
g_x
gx为
x
x
x到
T
T
T最长路,把每条边的边权设为
f
x
+
1
+
g
y
f_x+1+g_y
fx+1+gy,那么一个割集中的边权最大值就是最长路。
枚举删哪一个点,考虑如何维护这个割集,使得割集中的边权不会受被删点的影响。删去
x
x
x后,有三种边是不会受影响的的,
S
S
S到拓扑序在
x
x
x之前的点的边,拓扑序在
x
x
x后到
T
T
T的边,和拓扑序在
x
x
x之前的点到拓扑序在
x
x
x后的点边。
所以可以按照拓扑序逐个枚举被删的点,用一个堆维护割集即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=500010;
const int Maxm=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,m,deg[Maxn],a[Maxn],la=0,f[Maxn],g[Maxn],ans1,ans2=inf;
vector<int>p1[Maxn],p2[Maxn];
struct Heap
{
priority_queue<int>q1,q2;
int Top()
{
while(!q2.empty()&&q1.top()==q2.top())q1.pop(),q2.pop();
return q1.top();
}
void Delete(int x){q2.push(x);}
void Push(int x){q1.push(x);}
}Q;
int main()
{
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
p1[x].push_back(y);deg[y]++;
p2[y].push_back(x);
}
queue<int>q;
for(int i=1;i<=n;i++)if(!deg[i])q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();
a[++la]=x;
for(int i=0;i<p1[x].size();i++)
{
int y=p1[x][i];
deg[y]--;
if(!deg[y])q.push(y);
}
}
for(int i=1;i<=n;i++)
for(int j=0;j<p1[a[i]].size();j++)
f[p1[a[i]][j]]=max(f[p1[a[i]][j]],f[a[i]]+1);
for(int i=n;i;i--)
for(int j=0;j<p2[a[i]].size();j++)
g[p2[a[i]][j]]=max(g[p2[a[i]][j]],g[a[i]]+1);
for(int i=1;i<=n;i++)Q.Push(g[i]);
for(int i=1;i<=n;i++)
{
int x=a[i];
for(int j=0;j<p2[x].size();j++)Q.Delete(f[p2[x][j]]+g[x]+1);
Q.Delete(g[x]);
if(Q.Top()<ans2)ans2=Q.Top(),ans1=x;
for(int j=0;j<p1[x].size();j++)Q.Push(f[x]+g[p1[x][j]]+1);
Q.Push(f[x]);
}
printf("%d %d",ans1,ans2);
}
Ant colony:
树形DP水题,每个点记录合法范围即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=1000010;
const int inf=1000000000;
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],X,Y,L[Maxn],R[Maxn],d[Maxn];
struct Edge{int y,next;}e[Maxn<<1];
int last[Maxn],len=0;
void ins(int x,int y)
{
int t=++len;
e[t].y=y;e[t].next=last[x];last[x]=t;
}
void dfs(int x,int fa)
{
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
if(y==fa)continue;
if((LL)L[x]*(d[x]-1)>inf)L[y]=inf+1;
else L[y]=L[x]*(d[x]-1);
if((LL)R[x]*(d[x]-1)+max(d[x]-2,0)>inf)R[y]=inf;
else R[y]=R[x]*(d[x]-1)+max(d[x]-2,0);
dfs(y,x);
}
}
int main()
{
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)a[i]=read();
sort(a+1,a+1+m);
for(int i=1;i<n;i++)
{
int x=read(),y=read();d[x]++,d[y]++;
if(i==1)X=x,Y=y;
ins(x,y),ins(y,x);
}
L[X]=R[X]=L[Y]=R[Y]=k;
dfs(X,Y),dfs(Y,X);
LL ans=0;
for(int i=1;i<=n;i++)
if(d[i]==1)
{
if(L[i]==inf+1)continue;
int l,r,ll,rr;
l=1,r=m;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]>=L[i])r=mid-1;
else l=mid+1;
}
ll=r+1;
l=1,r=m;
while(l<=r)
{
int mid=l+r>>1;
if(a[mid]<=R[i])l=mid+1;
else r=mid-1;
}
rr=l-1;
ans+=((LL)rr-ll+1);
}
ans*=(LL)k;
printf("%lld",ans);
}