今天又差点垫底,下次要加油啊。
T1:tom
这道题,其实转化过来,就是2个连通块,一个为a,一个为b,且a和-b要连通,我们需要找到树上的一条边,将树分为大小为a,b的两部分,对两部分分别染色即可,于是我们先dfs一遍,找到每个点的子树大小,再dfs边找它上面的点个数,若遍历到一个子树大小为a或b,就从它开始,它的父节点为-b或a,再遍历这棵子树,标上序号;若无子树为a或b,就开始遍历,若有一点上面的点个数为a或b,就开始标记。
代码:
#include<bits/stdc++.h>
#define ll long long
#define db double
#define re register
#define cs const
#define N 100005
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
bool mark;
int n,a,b,u,v,cnt,tot;
int first[N],net[2*N],to[2*N],size[N],up[N],num[N];
void add(int x,int y)
{
net[++tot]=first[x];
first[x]=tot;
to[tot]=y;
}
void dfs1(int x,int fa)
{
for(int e=first[x];e;e=net[e])
{
int v=to[e];
if(v==fa) continue;
dfs1(v,x);
size[x]+=size[v];
}
size[x]++;
}
void make(int fa,int o,int bj)
{
for(int e=first[o];e;e=net[e])
{
int v=to[e];
if(v==fa) continue;
make(o,v,bj);
}
if(bj) num[o]=++cnt;
else num[o]=--cnt;
}
void dfs2(int x,int fa)
{
if(mark) return;
up[x]=size[fa]-size[x]+up[fa];
if(x==1) up[x]=0;
if(up[x]==a)
{
make(x,fa,1);
cnt=0;
for(int e=first[x];e;e=net[e])
{
int v=to[e];
if(v==fa)continue;
make(x,v,0);
}
mark=1;
num[x]=--cnt;
return;
}
if(up[x]==b)
{
make(x,fa,0);
cnt=0;
for(int e=first[x];e;e=net[e])
{
int v=to[e];
if(v==fa)continue;
make(x,v,1);
}
mark=1;
num[x]=++cnt;
return;
}
for(int e=first[x];e;e=net[e])
{
int v=to[e];
if(v==fa) continue;
if(size[v]==a)
{
make(x,v,1);
cnt=0;
for(int ee=first[x];ee;ee=net[ee])
{
int vv=to[ee];
if(vv==v)continue;
make(x,vv,0);
}
mark=1;
num[x]=--cnt;
return;
}
if(size[v]==b)
{
make(x,v,0);
cnt=0;
for(int ee=first[x];ee;ee=net[ee])
{
int vv=to[ee];
if(vv==v)continue;
make(x,vv,1);
}
mark=1;
num[x]=++cnt;
return;
}
dfs2(v,x);
if(mark) return;
}
}
int main()
{
n=read();
a=read();
b=read();
for(int i=1;i<n;i++)
{
u=read();
v=read();
add(u,v);
add(v,u);
}
dfs1(1,0);
dfs2(1,0);
if(!mark) printf("-1");
else
{
for(int i=1;i<=n;i++) printf("%d ",num[i]);
}
}
T2:jerry
我们可以很容易地得出一个结论,即,括号加在加号前无任何贡献。所以我们可以把连着的正数都加在一起,使整个数列转化为正负负负正负负正负负负负负…。
接着我们预处理下后缀的绝对值和,从后往前dp。
接下来讨论
1.a[i]>0不管
2.a[i]<0
①:a[i+1]<0负负,除了当前是负直接加sufsum(i+1);
②:a[i+1]>0负正负。两种选择:不加,或者加了负的这两个,获得后面的sufsum(i+2)。
详细论述见wyh巨佬链接,因为我实在口胡不下去了
代码:
#include<bits/stdc++.h>
#define int long long
#define db double
#define re register
#define cs const
#define N 200005
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int sum[N],a[N],b[N],f[N];
int cnt,n,t;
signed main()
{
t=read();
while(t--)
{
cnt=0;
n=read();
a[0]=-1;
for(re int i=1;i<=n;i++)
{
f[i]=0;
a[i]=read();
}
for(re int i=1;i<=n;i++)
{
if(a[i]<0) b[++cnt]=a[i];
else if(a[i-1]<0) b[++cnt]=a[i];
else b[cnt]+=a[i];
}
sum[cnt+1]=0;
for(re int i=cnt;i>=1;i--) sum[i]=sum[i+1]+abs(b[i]);
f[cnt]=b[cnt];
for(re int i=cnt-1;i>=1;i--)
{
if(b[i]>=0) f[i]=f[i+1]+b[i];
else
{
if(b[i+1]<0) f[i]=b[i]+sum[i+1];
else f[i]=max(f[i+1]+b[i],sum[i+2]+b[i]-b[i+1]);
}
}
printf("%lld\n",f[1]);
}
}
T3:speike
可以猜想出两个结论,一,我们不会走回头路,会一直沿着x轴正方向前进,二,右边的那条边没用,我们可以删去,且我们总是会沿着矩形边界走,接下来我们就可用扫描线(我还不会 )解决问题。
代码(来自wyh巨佬):
//感性理解:沿着线段走总是没错的,,反正只需要算y轴的贡献。总不能横着走回去。
//线段sort,把起点的假线段放在第一个。然后动态规划。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define re register
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}return cnt*f;
}
struct node{
int l,r,tag,key;
}t[5000003];
int pre[1000003][2];
int f[1000003][2];
int n,end;
struct Line{
int down,up,x;
}line[1000003];
int lcnt;
int bb[4000003],bcnt,len;
bool linecm(Line a,Line b){
if(a.x!=b.x)return a.x<b.x;
if(a.down!=b.down)return a.down<b.down;
return a.up<b.up;
}
inline void build(int u,int l,int r){
t[u].l=l;t[u].r=r;
if(l==r){
return;
}int mid=(l+r)>>1;
build(u*2,l,mid);build(u*2+1,mid+1,r);
}
inline void pushdown(int u){
if(t[u].tag){
t[u*2].tag=t[u*2+1].tag=t[u].tag;
t[u*2].key=t[u*2+1].key=t[u].tag;
t[u].tag=0;
}
}
inline void modify(int u,int ql,int qr,int key){
if(t[u].r<ql||t[u].l>qr)return;
if(ql<=t[u].l&&t[u].r<=qr){
t[u].tag=t[u].key=key;return;
}pushdown(u);
modify(u*2,ql,qr,key);modify(u*2+1,ql,qr,key);
}
inline int query(int u,int pos){
if(t[u].l==t[u].r&&t[u].r==pos)return t[u].key;
pushdown(u);
int mid=(t[u].l+t[u].r)>>1;
if(pos<=mid)return query(u*2,pos);else return query(u*2+1,pos);
}
signed main(){
memset(f,0x3f,sizeof(f));
n=in;
end=in;
line[++lcnt]={0,0,0};
line[++lcnt]={0,0,end};
for(re int i=1;i<=n;i++){
int a=in;int b=in;int c=in;int d=in;
if(b<d)swap(b,d);bb[++bcnt]=b;bb[++bcnt]=d;
line[++lcnt]={d,b,a};
}
bb[++bcnt]=0;
sort(line+1,line+lcnt+1,linecm);
sort(bb+1,bb+bcnt+1);
len=unique(bb+1,bb+bcnt+1)-bb-1;
for(re int i=1;i<=lcnt;i++){
line[i].down=lower_bound(bb+1,bb+len+1,line[i].down)-bb;
line[i].up=lower_bound(bb+1,bb+len+1,line[i].up)-bb;
}
for(re int i=2;i<=lcnt;i++){
if(line[i].x)break;if(!bb[line[i].down]&&!bb[line[i].up]){swap(line[i],line[1]);break;}
}
build(1,1,len);
for(re int i=1;i<=lcnt;i++){
pre[i][0]=query(1,line[i].down);pre[i][1]=query(1,line[i].up);
modify(1,line[i].down,line[i].up,i);
}
f[1][0]=f[1][1]=0;
for(re int i=2;i<=lcnt;i++){
int p=max(1,pre[i][0]);
f[i][0]=min(f[p][0]+abs(bb[line[i].down]-bb[line[p].down]),f[p][1]+abs(bb[line[i].down]-bb[line[p].up]));
p=max(1,pre[i][1]);
f[i][1]=min(f[p][0]+abs(bb[line[i].up]-bb[line[p].down]),f[p][1]+abs(bb[line[i].up]-bb[line[p].up]));
}
cout<<f[lcnt][0]+end;
return 0;
}