A. 歐幾里得的噩夢
線性基,不會,咕咕咕
B. 清掃
賽時想到要分為兩種操作了,也想到要把剩餘未分配石頭上傳處理了,但是沒想清楚操作之間的關係,衝著一堆樣例的結論
就開始寫分討了,思路不是很清晰,然後因為綁包。。。似的很慘
對於一個節點,我們把它直接兒子當作葉子即可,上面的資訊是其子樹分配後需要處理的石頭數,對於消去操作,可以選擇
兩個其子樹內的葉子消,這樣子樹內的石頭會2倍的減少,也可以一個子樹內,一個子樹外,,這樣就是一起減少1,我們發現
根節點石頭數必須小於其子樹石頭總數才可能有解,所以我們應該先選子樹內的節點消,直到子樹石頭總數與根相同停止(能在
子樹內處理的就不在子樹外處理了),還有一些對節點數的限制,細節看程式碼吧
點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
const int maxn=1e5+10;
using namespace std;
int n,a[maxn],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,in[maxn],rt;
void add(int x,int y)
{
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void addm(int x,int y)
{
add(x,y),add(y,x);
}
void lsx(int x,int fa)
{
int sum=0,temp=0,t=0;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==fa) continue;
lsx(y,x);
temp++,sum+=a[y],t=max(t,a[y]);
}
if(!temp)return;
if(a[x]<ceil(1.0*sum/2)||sum<a[x]||t>a[x])
{
puts("NO");
exit(0);
}
// cout<<x<<" "<<a[x]<<" "<<sum<<endl;
a[x]=a[x]*2-sum;
}
signed main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++)
{
int x,y;
cin>>x>>y;
in[x]++,in[y]++;
addm(x,y);
if(in[x]>1)rt=x;
if(in[y]>1)rt=y;
}
if(n==2)
{
if(a[1]==a[2])puts("YES");
else puts("NO");
return 0;
}
lsx(rt,0);
if(a[rt]==0)puts("YES");
else puts("NO");
return 0;
}
/*
3
2 1 3
1 2
1 3
*/
C. 購物
先對陣列排序,考慮新加進來一個數 \(a_i\) 的影響,它的統治區間是 \(\lceil\frac{a_i}{2}\rceil - a_i\),用 \(sum\) 表示字首和,若 \(\lceil\frac{a_i}{2}\rceil\) 大於 \(sum_{i-1}\)
則在 $ sum_{i-1} - \lceil \frac{a_i}{2} \rceil $ 處產生了一段缺口,而 \(\lceil\frac{a_i}{2}\rceil - sum_i\) 一定是連續的,小於 \(a_i\) 的部分可以用 \(a_i\) 理解,而對於大於
\(a_i\) 的部分,因為 \(\lceil \frac{a_i}{2} \rceil>sum_{i-1}\) 所以這部分數都可以用 \(a_i+sum_{i-1}\) 理解
點選檢視程式碼
#include<bits/stdc++.h>
#define int long long
const int maxn=1e5+10;
using namespace std;
int a[maxn],sum[maxn],n,q[maxn],l,r,ans;
signed main()
{
freopen("buy.in","r",stdin);
freopen("buy.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
ans=sum[n];
for(int i=1;i<=n;i++)
{
if(ceil(1.0*a[i]/2)>sum[i-1])
{
ans-=(ceil(1.0*a[i]/2)-sum[i-1]-1);
}
}
cout<<ans;
return 0;
}
/*
3
181573206 28406510 456313
134537153
*/
D. ants
回滾莫隊板子題,但是我不會。。。
只加不減的莫隊,用並查集維護聯通塊實現加的操作
點選檢視程式碼
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct lsx{int l,r,pos,id;}q[maxn];
int n,m,a[maxn],ans[maxn],st[maxn],size[maxn],fa[maxn],res,top;
bool vis[maxn];
bool cmp(lsx a,lsx b){return a.pos==b.pos?a.r<b.r:a.pos<b.pos;}
int find(int x){return x==fa[x]?x:find(fa[x]);}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y)return ;
if(size[x]>size[y])x^=y^=x^=y;
fa[x]=y;
size[y]+=size[x];
st[++st[0]]=x;
}
void add(int x)
{
vis[x]=1;
if(vis[x-1]) merge(x,x-1);
if(vis[x+1]) merge(x,x+1);
res=max(res,size[find(x)]);
}
void del()
{
while(st[0]>top)
{
size[find(st[st[0]])]-=size[st[st[0]]];
fa[st[st[0]]]=st[st[0]];
st[0]--;
}
}
int main(){
freopen("ants.in","r",stdin);
freopen("ants.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
int t=sqrt(n);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++)
{
cin>>q[i].l>>q[i].r;
q[i].pos=(q[i].l-1)/t+1;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int l,r,pos;
for(int i=1;i<=m;i++)
{
if(q[i].pos!=q[i-1].pos)
{
for(int j=1;j<=n;j++)
{
fa[j]=j;
vis[j]=0;
size[j]=1;
}
res=st[0]=0;
l=pos=q[i].pos*t+1;
r=l-1;
}
if(q[i].pos==(q[i].r-1)/t+1)
{
int now=1,maxx=1;
for(int j=q[i].l;j<=q[i].r;j++) st[++st[0]]=a[j];
sort(st+1,st+1+st[0]);
for(int j=1;j<=st[0];j++)
{
if(st[j]==st[j-1]+1&&j>1) now++;
else now=1;
maxx=max(maxx,now);
}
ans[q[i].id]=maxx;
st[0]=0;
}
else
{
while(r<q[i].r) add(a[++r]);
int flag=res;
top=st[0];
while(l>q[i].l) add(a[--l]);
ans[q[i].id]=res;
res=flag;
while(l<pos) vis[a[l++]]=0;
del();
}
}
for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
return 0;
}