来中山纪中半个月了,差不多就要结束了,
写一些之前考试能更正的题解吧,还有一些不是给人做的(比如IOI2018互测。。
备注:我不会的就没有放上来了,所有数学有关的基本上都死了。
所以这里的题目都是相对其他考的而言简单的题qwq,然而我考场上还是不会做
写完之后发现只会分块和线段树qwq,偶尔有点网络流,其他都不会,awsl
3.18
第一天来就考NOI NO.2的题目。。
这道题非常奇妙的地方在于
因为(n)是固定的所以(n+1)本就是不需要求的状态,
然后对于(O(n^2))就是(f_{n+1,i}=f_{n,i}*p^i+f_{n,i-1}*(1-p)^{n-i+1})
利用(f_{n+1,i})是可以从两个方程中转移过来的,我们就可以得出有效状态(f_{n,i})了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#define ll long long
using namespace std;
const ll N=1e6+6;
const ll mod=998244353;
ll n,p,inv[N],finv[N],f[N];
ll read(){
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void init(){
inv[1]=1;finv[1]=1;f[1]=1;
for(ll i=2;i<=n;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(ll i=2;i<=n;i++)finv[i]=finv[i-1]*inv[i]%mod;
for(ll i=2;i<=n;i++)f[i]=f[i-1]*i%mod;
}
ll ksm(ll x,ll k){
ll ans=1;
while(k){
if(k&1)ans=(ans*x)%mod;x=(x*x)%mod;k>>=1;
}return ans%mod;
}
int main(){
freopen("more.in","r",stdin);
freopen("more.out","w",stdout);
n=read();p=read();init();
if(p==499122177){
for(ll i=1;i<n;i++){
ll sum=((finv[n-i]*finv[i])%mod)*f[n]%mod;
sum=(sum%mod*ksm(ksm(p,n-i)%mod,i))%mod;
printf("%lld ",sum);
}
}
else {
ll sum=1,tmp;
for(ll i=1;i<n;i++){
tmp=ksm(((ksm(p,i)-ksm((1-p+mod)%mod,i)+mod)%mod),mod-2)%mod;
sum=sum*(tmp*((ksm(p,n-i+1)-ksm((1-p+mod)%mod,n-i+1)+mod)%mod)%mod)%mod;
printf("%lld ",sum);
}
}
return 0;
}
3.19
唯一比较容易的一天qwq
T1
数据结构维护dp
第一题就是要下放很多的线段求满足条件的下方,二位偏序,然后卡线段树。
二位偏序排个序就好了(我不会说我这个没想到qwq)
线段树维护dp最大值->树状数组维护
什么?你说树状数组(log^2),因为这里我们的序列是单调的,所以直接下放直接查就可以了,这种树状数组只能在这种情况下用。
别人线段树比我树状数组快系列qwq
哦对了,因为这道题叫fc,然后在Windows下面对拍也是fc,就变成了跑了两次这个代码,并没有比较文件输出。所以它全程没给我跑出错误,搞得我当时以为A了。
#include<bits/stdc++.h>
#define ll long long
#define ls root<<1
#define rs root<<1|1
#define max(a,b) a>b?a:b
using namespace std;
const ll N=1e6+5;
ll vis[21],n,m,ti[N],ai[N],ans,s[21];
ll f[N],id[N];
ll read(){
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void work_20(){
for(ll i=0;i<(1<<n);i++){
ll sum=0,flag=0;memset(vis,0,sizeof(vis));
for(ll j=1;j<=n;j++)if((1<<(j-1))&i)vis[j]=1;
for(ll j=1;j<=n;j++)s[j]=s[j-1]+vis[j];
for(ll j=1;j<=n;j++)
if(vis[j]&&((s[min(j+ti[j],n)]-s[j-1]>1)||(s[j]-s[max(0ll,j-ti[j]-1)]>1)))flag=1;
if(flag)continue;
for(ll j=1;j<=n;j++)if(vis[j])sum+=ai[j];
ans=max(sum,ans);
}
cout<<ans<<endl;
}
void work_15(){
ll ans=0;
for(ll i=1;i<=n;i++)f[i]=ai[i];
for(ll i=1;i<=n;i++)
for(ll j=0;j<i;j++){
if(i-ti[i]<=j||ti[j]+j>=i)continue;
f[i]=max(f[i],f[j]+ai[i]);
}
for(int i=1;i<=n;i++)ans=max(ans,f[i]);
cout<<ans<<endl;
}
ll mx[N<<2],mx2[N<<2],lazy[N<<2];
void update(int root,int left,int right,int v){
if(left==right){mx[root]=f[left];return ;}
int mid=(left+right)>>1;
if(mid>=v) update(ls,left,mid,v);
if(mid<v) update(rs,mid+1,right,v);
mx[root]=max(mx[ls],mx[rs]);
}
ll query(int root,int left,int right,int l,int r){
if(left>r||right<l)return -99999999;
if(left>=l&&right<=r)return mx[root];
ll a=-99999999,b=-99999999,mid=(left+right)>>1;
if(mid>=l) a=query(ls,left,mid,l,r);
if(mid<r) b=query(rs,mid+1,right,l,r);
return max(a,b);
}
bool cmp(int a,int b){
return a+ti[a]<b+ti[b];
}
ll c[N];
void add(ll x){
for(ll i=x;i<=n;i+=(i&(-i)))c[i]=max(c[i],f[x]);
}
ll ask(ll x){
ll ans=0;for(ll i=x;i>=1;i-=(i&(-i)))ans=max(c[i],ans);return ans;
}
int main(){
freopen("fc.in","r",stdin);
freopen("fc.out","w",stdout);
n=read();ll flag=1;
for(int i=1;i<=n;i++){ti[i]=read();if(ti[i]!=ti[1])flag=0;}
for(int i=1;i<=n;i++)ai[i]=read()*ti[i];
for(int i=1;i<=n;i++)ti[i]--,id[i]=i;
if(n<=20){work_20();return 0;}
if(n<=5000){work_15();return 0;}
for(ll i=1;i<=n;i++)f[i]=ai[i];
if(flag){
for(ll i=1;i<=n;i++){
if(i-ti[i]-1>=1)
f[i]=query(1,1,n,1,i-ti[i]-1)+ai[i];
update(1,1,n,i);
}
}else {
sort(id+1,id+n+1,cmp);int now=1;
for(int i=1;i<=n;i++){
while(id[now]+ti[id[now]]<i&&now<=n)add(id[now]),now++;
if(i-ti[i]-1>=1)f[i]=ask(i-ti[i]-1)+ai[i];
}
}
for(int i=1;i<=n;i++)ans=max(ans,f[i]);
printf("%lld
",ans);
return 0;
}
T2
就是因为这个东西,本来我已经放弃学SAM了的qwq。
是关于SAM的模板题吧qwq
就是考关于SAM的拼接。因为每个序列只能贡献连续的一段,所以我们从n到1把每个字符串的SAM连接起来。就是建的时候记录一下左右端点。然后拼起来,具体看代码吧。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=4e6+5;
const int mod=1e9+7;
int f[N],n,a[N],tot,last,tmp[5],rt,ls[N],rs[N];
char s[N],ss[N],b[N];
struct node{
int ch[7],len,ff;
}t[N];
int dfs(int x){
if(f[x]!=-1)return f[x];
f[x]=1;
for(int i=1;i<=4;i++){
f[x]+=dfs(t[x].ch[i]);
f[x]%=mod;
}return f[x];
}
void print(int x,int k){
puts(ss);
for(int i=1;i<=4;i++){
if(t[x].ch[i]){
ss[k]=b[i];
print(t[x].ch[i],k+1);
}
}ss[k]=0;
}
void update(int x){
int p=last,np=last=++tot;
t[np].len=t[p].len+1;
while(p&&!t[p].ch[x])t[p].ch[x]=np,p=t[p].ff;
if(!p)t[np].ff=rt;
else {
int q=t[p].ch[x];
if(t[q].len==t[p].len+1)t[np].ff=q;
else{
int nq=++tot;t[nq]=t[q];
t[nq].len=t[p].len+1;t[q].ff=t[np].ff=nq;
while(p&&t[p].ch[x]==q){t[p].ch[x]=nq;p=t[p].ff;}
}
}
}
int main(){
freopen("copy.in","r",stdin);
freopen("copy.out","w",stdout);
memset(f,-1,sizeof(f));f[0]=0;
a['A']=1;a['C']=2;a['G']=3;a['T']=4;
b[1]='A';b[2]='C';b[3]='G';b[4]='T';
cin>>n;
for(int i=1;i<=n;i++){
ls[i]=last=rt=++tot;
scanf("%s",s+1);int len=strlen(s+1);
for(int i=1;i<=len;i++)update(a[s[i]]);
rs[i]=tot;
}
for(int i=n;i>=1;i--){
for(int j=rs[i];j>=ls[i];j--){
for(int k=1;k<=4;k++){
if(!t[j].ch[k])t[j].ch[k]=tmp[k];
}
}
for(int k=1;k<=4;k++)
tmp[k]=t[ls[i]].ch[k];
}
int k;cin>>k;
if(k)print(1,0);
printf("%d
",dfs(1));
return 0;
}
T3
卡常有点恶心啊qwq
首先对于子序列我们需要用序列自动机(这并不是一个很高大上的东西)维护,不然你会跑很多的重复子序列
肉眼可见的二分图匹配。但是如果我们把每个字符串的子序列都提出来连边建图不就炸了吗
所以套路就在于我们对于每个字符串只要搞出300个关于它的子序列就可以了。
因为这样就一定可以全部匹配了。
好了目前时间复杂度(O(n^4)),二分啊。
反正我的程序被卡了一点点时限,也可能有点错误吧,所以我有一个点是打表的。
然后网络流是一定跑不过的别想了,然后我必须搞出1000个关于它的子序列才可以?
如果有大佬可以看看我错哪里,蒟蒻感激不尽
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<map>
#include<vector>
#include<queue>
using namespace std;
vector<int>q[305][305];
map<string,int>mp,mp2;
const int N=301*301*50+5;
const int inf=1e9;
int n,tot,head[301],s,t,ans,cnt,nex[301][31],idx,a[301],ai[301];
int num=1,dep[N],line[N],Q[100001],vis[100001];
string ss[N],sss;
struct node{
int to,nex,c;
}e[N];
void add(int from,int to){
e[++num].to=to;e[num].nex=head[from];head[from]=num;
}
int dfs(int x){
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;if(vis[v])continue;vis[v]=1;
if(!Q[v]||dfs(Q[v])){Q[v]=x;return 1;}
}return 0;
}
int get(){
int ans=0;
for(int i=1;i<=n;i++){for(int i=1;i<=tot;i++)vis[i]=0;ans+=dfs(i);}
return ans;
}
int find(){
int l=1,r=n;ans=-1;
while(r>=l){
int mid=(l+r+1)>>1;
num=0;memset(head,0,sizeof(head));for(int i=1;i<=tot;i++)Q[i]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=mid;j++)
for(int k=0;k<q[i][j].size();k++)
add(i,q[i][j][k]);//add(q[i][j][k],i);
}
if(get()==n){
r=mid-1;for(int i=n+1;i<=tot;i++)if(Q[i])line[Q[i]]=i;ans=mid;
}
else l=mid+1;
}
}
//void dfs(int id,int now,int x,int y,string S){
// if(cnt>310||now>sss.size())return ;
//// cerr<<id<<' '<<now<<' '<<x<<' '<<y<<' '<<S<<endl;
// if(x>=y){
// if(!mp[S])mp[S]=++tot,ss[tot]=S;
// if(!vis[mp[S]])cnt++,q[id][y].push_back(mp[S]),vis[mp[S]]=1;return ;
// }dfs(id,now+1,x+1,y,S+sss[now]);dfs(id,now+1,x,y,S);
//}
void init(){
int n=sss.size();
for(int i=n;i>=1;i--){
for(int j=1;j<=26;j++)
nex[i-1][j]=nex[i][j];
if(i!=n)nex[i-1][sss[i]-'a'+1]=i;
}
}
void dfs(int id,int x,int y,int limit,string S){
if(cnt>1000||x>sss.size())return ;
if(y>=limit){
cnt++;
if(!mp[S])mp[S]=++tot,ss[tot]=S;q[id][limit].push_back(mp[S]);
return ;
}
for(int i=1;i<=26;i++)
if(nex[x][i])dfs(id,nex[x][i],y+1,limit,S+(char)(i+'a'-1));
}
int main(){
freopen("diff.in","r",stdin);
freopen("diff.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n;tot=n;int flag=0;
for(int i=1;i<=n;i++){
cnt=0;
cin>>sss;int l=sss.size();
if(sss=="hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")flag=1;
if(!flag){
init();string S;S+=sss[0];
for(int j=1;j<=l;j++){
dfs(i,0,1,j,S);dfs(i,0,0,j,"");if(cnt>1000)break;
}
}
if(flag){
ai[i]=sss.size();
while(a[ai[i]])ai[i]--;a[ai[i]]=1;
}
}
if(flag){
cout<<300<<endl;
for(int i=1;i<=n;i++){
for(int j=1;j<=ai[i];j++)
cout<<'h';
cout<<endl;
}return 0;
}
s=0,t=tot+1,find();
if(ans==-1){cout<<ans<<endl;return 0;}
else cout<<ans<<endl;
for(int i=1;i<=n;i++)cout<<ss[line[i]]<<endl;
return 0;
}
3.20
今天自闭了,没交,盯着电脑看了四个小时。
网上有题解自己搜吧,据说还是错的。
就是因为区间有包含情况所以不能这么转移。
第二题就是加强版天天爱跑步,给你一颗树,m条路径,问你路径相交的个数,注路径有起点时间和终点时间,不相交于点上也是可以的,gaojiaxuan说还可以加强???
第三题就是较强版八邻桥,八邻桥阵2333,就是变成了m条河道,你要怎么修。
3.22
又是自闭的一天。
第一题构造。没得人写,写的都是随机。
第二题多项式相关,wsl
第三题,考场上算错复杂度了算出来的是(O(n*logn*sqrt n))
其实是(O(n*logsqrt n*sqrt n))
就是对于题目,我每次修改只会多出两个区间来,那么用set维护区间,对于每个区间直接((log^2))改,因为均摊下来最多((n+q))个区间所以是接近(O(n*log^2))的,主席树维护。
好了说一大堆,其实我根本没用这个方法。还是分块好.jpg
对于分块,直接维护每个块的乘积,然后修改的话打标记,修改时因为每个整块有b满了和没满的,所以维护b满了的一个数组,然后块内二分当前满了的然后快速幂没满的。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e6+5;
const ll mod=1e9+7;
ll l[N],r[N],ans,a[N],b[N],c[N];
ll mul[N],s[N],bl[N],f[N];
ll n,q,tmp,mx[N];
ll read(){
ll x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
ll ksm(ll x,ll k){
ll ss=1;
while(k){if(k&1)ss=(ss*x)%mod;x=(x*x)%mod;k>>=1;}
return ss%mod;
}
void build(){
tmp=sqrt(n);
for(ll i=1;i<=tmp;i++)
l[i]=(i-1)*tmp+1,r[i]=tmp*i;
if(r[tmp]<n)tmp++;
l[tmp]=r[tmp-1]+1,r[tmp]=n;
for(ll i=1;i<=tmp;i++){
for(ll j=l[i];j<=r[i];j++)
mx[i]=max(mx[i],a[j]),bl[j]=i,c[j]=b[j];sort(c+l[i],c+r[i]+1);
for(ll j=l[i];j<=r[i];j++)
{mul[j]=c[j];if(j>l[i])mul[j]=(mul[j]*mul[j-1])%mod;}
ll sum=1;
for(ll j=l[i];j<=r[i];j++)
sum=(sum*min(b[j],a[j]))%mod;s[i]=sum;
}
}
void change(ll x,ll y){
b[x]=y;s[bl[x]]=1;
for(ll i=l[bl[x]];i<=r[bl[x]];i++)c[i]=b[i];
sort(c+l[bl[x]],c+r[bl[x]]+1);
for(ll i=l[bl[x]];i<=r[bl[x]];i++)
{mul[i]=c[i];if(i>l[bl[x]])mul[i]=(mul[i]*mul[i-1])%mod;}
if(f[bl[x]]){for(ll i=l[bl[x]];i<=r[bl[x]];i++)s[bl[x]]=(s[bl[x]]*min(f[bl[x]],b[i]))%mod;}
else for(ll i=l[bl[x]];i<=r[bl[x]];i++)s[bl[x]]=(s[bl[x]]*min(a[i],b[i]))%mod;
}
ll add(ll x,ll y){
f[x]=mx[x]=y;s[x]=1;
ll u=lower_bound(c+l[x],c+r[x]+1,y)-c-1;
if(u+1<=r[x])s[x]=(ksm(y,r[x]-u))%mod;
if(u>r[x])u=r[x];
if(u>=l[x])s[x]=(s[x]*mul[u])%mod;
}
int main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
n=read();q=read();
for(ll i=1;i<=n;i++)a[i]=read();
for(ll i=1;i<=n;i++)a[i]=max(a[i],a[i-1]);
for(ll i=1;i<=n;i++)b[i]=read();
build();
for(ll i=1;i<=q;i++){
ll opt=read();ans=1;
if(opt==0){
ll x=read(),y=read(),ss=0;
if(f[bl[x]])for(ll i=l[bl[x]];i<=r[bl[x]];i++)a[i]=max(a[i],f[bl[x]]);f[bl[x]]=0;
for(ll i=x;i<=r[bl[x]];i++)a[i]=max(a[i],y),mx[bl[x]]=max(a[i],mx[bl[x]]);
s[bl[x]]=1;for(ll i=l[bl[x]];i<=r[bl[x]];i++)s[bl[x]]=(s[bl[x]]*min(a[i],b[i]))%mod;
ll j=bl[x]+1;
while(mx[j]<y&&j<=tmp)add(j,y),j++;
if(j<=tmp){
for(ll k=l[j];k<=r[j];k++)a[k]=max(a[k],max(f[j],y));
s[j]=1;for(ll k=l[j];k<=r[j];k++)s[j]=(s[j]*min(a[k],b[k]))%mod;
}
for(ll j=1;j<=tmp;j++)ans=(ans*s[j])%mod;
printf("%lld
",ans%mod);
}else {
ll x=read(),y=read();change(x,y);
for(ll j=1;j<=tmp;j++)ans=(ans*s[j])%mod;
printf("%lld
",ans%mod);
}
}
return 0;
}
3.23
考了CJ他们的题目
本来就是因为CJ不收去的JZ。然后我在JZ考CJ。ZYYS
第一题。。。分类讨论后没打了,自我感觉和毒瘤很像
第二题。。。神仙组合不过被gaojiaxuan用了一个套路然后好像是可以多项式做出来,据会多项式的人说特别简单比题解良心多了。
考虑最小割。
S连体力流量为1,T连智力流量为1,然后把天数拆点,拆点之间连边流量为1。
然后按题目要求把天数和任务连inf边。
就是说要么这天必须选,要么就是这天要做的任务之前已经被做完了。
一个让人误认为是跑最大答案的网络流。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=1e5+5;
const int inf=1e9;
int num=1,head[N],k;
int vis[N],dep[N],ti[N],n,m,s,t;
struct node{
int to,nex,c;
}e[N<<1];
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void add(int from,int to,int c){
e[++num].to=to;e[num].c=c;e[num].nex=head[from];head[from]=num;
}
bool bfs(){
memset(dep,0,sizeof(dep));dep[s]=1;
queue<int>q;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(!dep[v]&&e[i].c)q.push(v),dep[v]=dep[u]+1;
}
}return dep[t];
}
int dfs(int x,int cap){
if(x==t)return cap;
int addx=0;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;
if(dep[v]==dep[x]+1&&e[i].c){
int tmp=dfs(v,min(e[i].c,cap-addx));
e[i].c-=tmp;e[i^1].c+=tmp;addx+=tmp;
}
}return addx;
}
int dinic(){
int ans=0;while(bfs())ans+=dfs(s,inf);return ans;
}
int main(){
freopen("deadline.in","r",stdin);
freopen("deadline.out","w",stdout);
n=read();m=read();k=read();s=0;t=m+m+n+1;
for(int i=1;i<=m;i++)add(i,i+m,1),add(i+m,i,0);
for(int i=1;i<=n;i++){
ti[i]=read();
if(ti[i])add(i+m+m,t,1),add(t,i+m+m,0);
else add(s,i+m+m,1),add(i+m+m,s,0);
}
for(int i=1;i<=k;i++){
int x=read(),y=read();
if(!ti[x]) add(x+m+m,y,inf),add(y,x+m+m,0);
else add(y+m,x+m+m,inf),add(x+m+m,y+m,0);
}
printf("%d
",dinic());
return 0;
}
3.25
没有更正题目。想知道题目问问租酥雨大佬他出的省选联考吧。
反正数学那么多我是自闭了。
3.26
好像是最大子权闭合图的模板题吧。
对于维护两颗不同的树共同连通块,我们考虑对于每一个根跑一次网络流,把它按照题目描述建边,也就是把两颗树的边放到一棵树里面,注意在网络流里的意思是,学了它,它往上的父亲也都被学了,这样就可以维护连通块了。
又不知道为什么,我建图的树边不能重复。也就是两点之间只能有一条inf边。但是fish和jeff的可以???
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int N=2e3+5;
const int inf=1e7;
int n,s,t,num,head[N],dep[N],ch[101][101];
int ans=0,sum=0,a[N],cnt=0;
struct edge{
int to,nex,c;
}e[N*100];
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void add(int from,int to,int c){
num++;e[num].to=to;e[num].c=c;e[num].nex=head[from];head[from]=num;
}
struct node{
int num=0,head[N];
edge e[N*100];
void clean(){memset(head,0,sizeof(head));num=0;}
void add(int from,int to){
num++;e[num].to=to;e[num].nex=head[from];head[from]=num;
}
}A,B;
void dfs1(int x,int ff){
for(int i=A.head[x];i;i=A.e[i].nex){
int v=A.e[i].to;if(v==ff)continue;
if(!ch[v][x])add(v,x,inf),add(x,v,0),ch[v][x]=1;dfs1(v,x);
}
}
void dfs2(int x,int ff){
for(int i=B.head[x];i;i=B.e[i].nex){
int v=B.e[i].to;if(v==ff)continue;
if(!ch[v][x])add(v,x,inf),add(x,v,0),ch[v][x]=1;dfs2(v,x);
}
}
bool bfs(){
memset(dep,0,sizeof(dep));dep[s]=1;int tot=0;
queue<int>q;q.push(s);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nex){
int v=e[i].to;
if(!dep[v]&&e[i].c)dep[v]=dep[u]+1,q.push(v);
}
}return dep[t];
}
int dfs(int x,int cap){
if(x==t)return cap;
int addx=0;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;cnt++;
if(dep[v]==dep[x]+1&&e[i].c){
int tmp=dfs(v,min(cap-addx,e[i].c));
e[i].c-=tmp;e[i^1].c+=tmp;addx+=tmp;
}
}return addx;
}
int dinic(){
int ans=0;while(bfs())cnt=0,ans+=dfs(s,inf);return ans;
}
void solve(){
n=read();ans=inf;sum=0;s=0;t=n+1;
A.clean();B.clean();
int ff=1;
for(int i=1;i<=n;i++)a[i]=read(),sum+=a[i]>0?a[i]:0;
for(int i=1;i<n;i++){
int x=read(),y=read();
A.add(x,y);A.add(y,x);
}
for(int i=1;i<n;i++){
int x=read(),y=read();
B.add(x,y);B.add(y,x);
}
for(int i=1;i<=n;i++){
memset(ch,0,sizeof(ch));
memset(head,0,sizeof(head));num=1;
for(int j=1;j<=n;j++){
if(a[j]>0)add(s,j,a[j]),add(j,s,0);
else add(j,t,-a[j]),add(t,j,0);
}
dfs1(i,i);dfs2(i,i);
int tmp=dinic();
ans=min(ans,tmp);
}printf("%d
",sum-ans);
}
int main(){
freopen("name.in","r",stdin);
freopen("name.out","w",stdout);
int T=read();
while(T--)solve();
return 0;
}
3.27
题解应该已经很清楚了吧qwq
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cmath>
#include<queue>
#define ls root<<1
#define rs root<<1|1
using namespace std;
const int N=5e5+5;
const int inf=1e9;
int lazy[N<<2],s[N<<2];
int n,m,num,head[N],wi[N],dep[N];
int size[N],fa[N],st[N][21],ed[N][21];
int visd[N],visb[N],idd[N],idb[N],bfsid,dfsid;
struct edge{
int to,nex;
}e[N<<1];
int read(){
int x=0,w=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*w;
}
void add(int from,int to){
num++;e[++num].to=to;e[num].nex=head[from];head[from]=num;
}
struct node{
int c[N];
void add(int x){for(int i=x;i<=n;i+=(i&(-i)))c[i]+=1;}
int query(int x){int ans=0;for(int i=x;i;i-=(i&(-i)))ans+=c[i];return ans;}
}T;
void dfs(int x){
idd[x]=++dfsid;visd[dfsid]=x;size[x]=1;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;if(dep[v])continue;
dep[v]=dep[x]+1;fa[v]=x;dfs(v);size[x]+=size[v];
}
}
void bfs(){
queue<int>q;q.push(1);
while(!q.empty()){
int x=q.front();q.pop();idb[x]=++bfsid;visb[bfsid]=x;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;if(v==fa[x])continue;q.push(v);
}
}
}
void init(int x){
int left=0,right=0;
for(int i=head[x];i;i=e[i].nex){
int v=e[i].to;if(v==fa[x])continue;init(v);
}st[x][0]=idb[x];ed[x][0]=idb[x];
for(int i=1;i<=20;i++){
left=right=0;
for(int j=head[x];j;j=e[j].nex){
int v=e[j].to;if(v==fa[x]||!st[v][i-1])continue;
if(!left)left=v;right=v;
}
st[x][i]=st[left][i-1];ed[x][i]=ed[right][i-1];
if(st[x][i]>ed[x][i])swap(st[x][i],ed[x][i]);
}
}
void build(int root,int l,int r){
if(l==r){
s[root]=wi[visb[l]];return ;
}int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
s[root]=min(s[ls],s[rs]);
}
void push(int root,int left,int right){
int mid=(left+right)>>1;
lazy[ls]+=lazy[root];lazy[rs]+=lazy[root];
s[ls]+=lazy[root];s[rs]+=lazy[root];
lazy[root]=0;return ;
}
void update(int root,int left,int right,int l,int r,int v){
if(left>=l&&right<=r){s[root]-=v;lazy[root]-=v;return ;}
if(lazy[root])push(root,left,right);
int mid=(left+right)>>1;
if(mid>=l) update(ls,left,mid,l,r,v);
if(mid<r) update(rs,mid+1,right,l,r,v);
s[root]=min(s[ls],s[rs]);
}
void update2(int root,int left,int right,int k){
if(left==right){s[root]=inf;return ;}
if(lazy[root])push(root,left,right);
int mid=(left+right)>>1;
if(mid>=k) update2(ls,left,mid,k);
if(mid<k) update2(rs,mid+1,right,k);
s[root]=min(s[ls],s[rs]);
}
int query(int root,int left,int right,int k){
int mid=(left+right)>>1;
if(left==right){return left;}
if(lazy[root])push(root,left,right);
if(s[ls]==k) return query(ls,left,mid,k);
else return query(rs,mid+1,right,k);
}
int ask(int root,int left,int right,int k){
int mid=(left+right)>>1;
if(left==right){return s[root];}
if(lazy[root])push(root,left,right);
if(mid>=k) return ask(ls,left,mid,k);
else return ask(rs,mid+1,right,k);
}
int main(){
freopen("pang.in","r",stdin);
freopen("pang.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)wi[i]=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);add(y,x);
}dep[1]=1;dfs(1);bfs();init(1);
build(1,1,n);
m=read();
for(int i=1;i<=m;i++){
int opt=read();
if(opt==1){
int x=read(),y=read(),d=0;
for(int i=0;i<=15;i++){if(st[x][i])update(1,1,n,st[x][i],ed[x][i],(int)(y/pow(2,i)));}
while(fa[x]&&(int)(y/pow(2,d))){
d++;
for(int i=1;i<=15;i++)
if(st[fa[x]][i]&&st[x][i-1]){update(1,1,n,st[x][i-1],ed[x][i-1],-(int)(y/pow(2,i+d)));}
x=fa[x];
for(int i=0;i<=15;i++)
if(st[x][i]){update(1,1,n,st[x][i],ed[x][i],(int)(y/pow(2,i+d)));}
}
while(s[1]<=0){int tmp;T.add(idd[visb[tmp=query(1,1,n,s[1])]]);update2(1,1,n,tmp);}
}else{
int x=read();
printf("%d
",T.query(idd[x]+size[x]-1)-T.query(idd[x]-1));
}
}
return 0;
}
后记
还有两天就要回来啦,省选退役预定了。