冲刺清北营 5
尝试了雀巢燃魂,感觉比较偏功能性导致豆似乎不是很好。虽然说咖啡因已经对我没什么太大作用了导致功能性也打折扣。咖啡可能会让你从醒着变成想睡觉,但是绝对不会让你从想睡觉的状态醒过来。等退役了以后有时间写个关于我喝过的咖啡的杂论。参考 joke3579 的意见是极好的。
乌蒙大象好像是大前天更新?
吃粮
设 \(dp_{x}\) 为从 \(x\) 开始随机游走到任意一个叶子的期望时间,则:
- 对于叶子:\(dp_x=a_x\)。
- 对于非叶子:\(dp_x=a_x+\dfrac 1{d_x}\sum_vdp_v\)
按照 [PKUWC2018]随机游走 的套路,设 \(dp_x=k_xdp_{fa_x}+b_x\),然后把后边的 \(dp_v\) 拆成 \(dp_{fa_x}\) 和 \(\sum dp_{son_x}\)。于是得到
\[k_x=\frac 1{d_x-\sum k_{son_x}} b_x=\frac{d_xa_x+\sum b_{son_x}}{d_x-\sum k_{son_x}}
\]
由于 \(1\) 没有父亲,所以答案就是 \(b_1\)。
然后看每次修改的影响:对于非叶子的贡献是从该节点到根的 \(k\) 的乘积乘上 \(d_xa_x\),对于叶子的贡献则是从该节点父亲到根的 \(k\) 的乘积乘上 \(d_xa_x\)。于是 dfs 两边得到每个节点贡献就可以 \(O(1)\) 修改了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
using namespace std;
const int mod=998244353;
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
struct node{
int v,next;
}edge[200010];
int n,t,head[100010];
void add(int u,int v){
edge[++t].v=v;edge[t].next=head[u];head[u]=t;
}
int d[100010],k[100010],b[100010],a[100010];
void dfs1(int x,int f){
if(d[x]==1){
k[x]=0;b[x]=a[x];return;
}
int sumk=0,sumb=0;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
dfs1(edge[i].v,x);
sumk=(sumk+k[edge[i].v])%mod;
sumb=(sumb+b[edge[i].v])%mod;
}
}
sumk=(d[x]-sumk+mod)%mod;
sumk=qpow(sumk,mod-2);
sumb=(sumb+1ll*d[x]*a[x])%mod;
k[x]=sumk;b[x]=1ll*sumb*sumk%mod;
}
int val[100010];
void dfs2(int x,int f){
if(d[x]==1){
val[x]=val[f];return;
}
val[x]=1ll*val[f]*k[x]%mod;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f)dfs2(edge[i].v,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);d[u]++;d[v]++;
}
val[0]=1;
dfs1(1,0);dfs2(1,0);
int ans=b[1];
printf("%d\n",ans);
int q;scanf("%d",&q);
while(q--){
int x,y;scanf("%d%d",&x,&y);
ans=(ans-1ll*val[x]*d[x]%mod*a[x]%mod+mod)%mod;
a[x]=y;
ans=(ans+1ll*val[x]*d[x]%mod*a[x])%mod;
printf("%d\n",ans);
}
return 0;
}
平衡
首先第一步 tarjan 缩点。然后每个连通块的代价是个下凸包。
然后不知道为啥要分治,但是分治了。考虑分治到当前层的图的最小权闭合子图,一定存在一种方案使所有点权 \(\ge mid\)。于是每次跑最小割分成两半左右递归。不知道为啥是对的。
实际上不用缩点。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int inf=0x3f3f3f3f;
vector<int>g[510];
struct node{
int v,w,next;
}edge[2000010];
int n,m1,m2,S,T,t,head[510];
void Add(int u,int v,int w){
edge[++t].v=v;edge[t].w=w;edge[t].next=head[u];head[u]=t;
}
void add(int u,int v,int w){
Add(u,v,w);Add(v,u,0);
}
int a[510],ans[510],id[510];
int head2[510],dis[510];
bool vis[510];
queue<int>q;
bool bfs(int st){
for(int i=0;i<=T;i++)head2[i]=head[i],dis[i]=0;
dis[st]=1;q.push(st);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=edge[i].next){
if(edge[i].w&&!dis[edge[i].v]){
dis[edge[i].v]=dis[x]+1;
if(edge[i].v==T){
while(!q.empty())q.pop();
return true;
}
q.push(edge[i].v);
}
}
}
return false;
}
int dfs(int x,int flow){
if(x==T)return flow;
int sum=0;
for(int &i=head2[x];i;i=edge[i].next){
if(edge[i].w&&dis[edge[i].v]==dis[x]+1){
int ret=dfs(edge[i].v,min(flow,edge[i].w));
if(ret){
edge[i].w-=ret;edge[i^1].w+=ret;
flow-=ret;sum+=ret;
if(!flow)break;
}
else dis[edge[i].v]=-1;
}
}
return sum;
}
void get(int x){
vis[x]=true;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].w&&!vis[edge[i].v])get(edge[i].v);
}
}
void solve(int l,int r,vector<int>p){
if(p.empty())return;
if(l==r){
for(int x:p)ans[x]=l;
return;
}
int mid=(l+r)>>1;
S=p.size();T=p.size()+1;t=1;
for(int i=0;i<=T;i++)head[i]=0,vis[i]=false;
for(int i=0;i<p.size();i++)id[p[i]]=i+1;
for(int i=0;i<p.size();i++){
if(a[p[i]]>mid)add(S,i,1);
else add(i,T,1);
for(int v:g[p[i]])if(id[v])add(i,id[v]-1,inf);
}
for(int x:p)id[x]=0;
while(bfs(S))dfs(S,inf);
get(S);
vector<int>L,R;
for(int i=0;i<p.size();i++){
if(!vis[i])L.push_back(p[i]);
else R.push_back(p[i]);
}
solve(l,mid,L);solve(mid+1,r,R);
}
int main(){
scanf("%d%d%d",&n,&m1,&m2);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m1;i++){
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v);g[v].push_back(u);
}
for(int i=1;i<=m2;i++){
int u,v;scanf("%d%d",&u,&v);
g[u].push_back(v);
}
vector<int>p;
for(int i=1;i<=n;i++)p.push_back(i);
solve(0,1e9,p);
long long val=0;
for(int i=1;i<=n;i++)val=(val+abs(a[i]-ans[i]));
printf("%lld\n",val);
return 0;
}
枸杞
蚌埠。懒得写了,直接粘题解。
依题解模拟即可。这题卡常,矩阵开 unsigned long long 然后矩阵乘的第三层改成乘一半取一次模即可。详见代码。
第三条命题咋证?
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <unordered_map>
#define int long long
using namespace std;
typedef vector<int> poly;
const int mod=998244353;
int read(){
int x=0;char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch))x=(10ll*x+ch-'0')%mod,ch=getchar();
return x;
}
void print(int x){
if(x>=10)print(x/10);
putchar(x%10+'0');
}
int n,m,wl,w[50010],jc[50010],inv[50010];
void get(int n){wl=1;while(wl<n)wl<<=1;}
#define add(x,y) (x+y>=mod?x+y-mod:x+y)
#define sub(x,y) (x<y?x-y+mod:x-y)
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return ans;
}
void init(int n){
int t=1;
while((1<<t)<n)t++;
t=min(t-1,21ll);
w[0]=1;w[1<<t]=qpow(31,1<<21-t);jc[0]=inv[0]=inv[1]=1;
for(int i=t;i>=1;i--)w[1<<i-1]=1ll*w[1<<i]*w[1<<i]%mod;
for(int i=1;i<(1<<t);i++)w[i]=1ll*w[i&i-1]*w[i&-i]%mod;
for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
}
inline void DIF(poly &a){
int n=a.size();
for(int mid=n>>1;mid;mid>>=1){
for(int i=0,k=0;i<n;i+=mid<<1,k++){
for(int j=0;j<mid;j++){
int x=a[i+j],y=1ll*a[i+j+mid]*w[k]%mod;
a[i+j]=add(x,y);a[i+j+mid]=sub(x,y);
}
}
}
}
inline void DIT(poly &a){
int n=a.size();
for(int mid=1;mid<n;mid<<=1){
for(int i=0,k=0;i<n;i+=mid<<1,k++){
for(int j=0;j<mid;j++){
int x=a[i+j],y=a[i+j+mid];
a[i+j]=add(x,y);a[i+j+mid]=1ll*sub(x,y)*w[k]%mod;
}
}
}
for(int i=1;i<=(n-1)>>1;i++)swap(a[i],a[n-i]);
int inv=qpow(n,mod-2);
for(int i=0;i<n;i++)a[i]=1ll*a[i]*inv%mod;
}
struct node{
unsigned long long a[31][31];
node(){memset(a,0,sizeof(a));}
node operator*(const node&s)const{
node ret;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=(n>>1);k++)ret.a[i][j]=ret.a[i][j]+a[i][k]*s.a[k][j];
ret.a[i][j]%=mod;
for(int k=(n>>1)+1;k<=n;k++)ret.a[i][j]=ret.a[i][j]+a[i][k]*s.a[k][j];
ret.a[i][j]%=mod;
}
}
return ret;
}
}gra,F[101],G[101];
const int sq=100,K=10000;
int tr[21][K+10],ans[21][21][K+10];
int getpow(int pw){
unsigned long long ans=0;
for(int i=1;i<=n;i++){
for(int k=1;k<=(n>>1);k++)ans=ans+F[pw%sq].a[i][k]*G[pw/sq].a[k][i];
ans%=mod;
for(int k=(n>>1)+1;k<=n;k++)ans=ans+F[pw%sq].a[i][k]*G[pw/sq].a[k][i];
ans%=mod;
}
return ans;
}
void prework(int x){
n=read();m=read();
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)gra.a[i][j]=0;
for(int i=1;i<=m;i++){
int u=read(),v=read();gra.a[u][v]=gra.a[v][u]=1;
}
for(int i=1;i<=n;i++)gra.a[i][i]=F[0].a[i][i]=G[0].a[i][i]=1;
for(int i=1;i<=sq;i++)F[i]=F[i-1]*gra;
G[1]=F[100];
for(int i=1;i<=sq;i++)G[i]=G[i-1]*G[1];
for(int i=0;i<=K;i++)tr[x][i]=getpow(i);
}
signed main(){
int T=read();init(K+1<<1);
for(int i=1;i<=T;i++)prework(i);
get(K+1<<1);
poly g(wl);
for(int i=0;i<=K;i++)g[i]=(i&1)?mod-inv[i]:inv[i];
DIF(g);
static int val[10010];
for(int i=1;i<=T;i++){
for(int j=0;j<=K;j++)val[j]=1;
for(int j=i;j<=T;j++){
for(int k=0;k<=K;k++)val[k]=1ll*val[k]*tr[j][k]%mod;
poly f(wl);
for(int k=0;k<=K;k++)f[k]=1ll*val[k]*inv[k]%mod;
DIF(f);for(int k=0;k<wl;k++)f[k]=1ll*f[k]*g[k]%mod;DIT(f);
for(int k=1;k<=K;k++)ans[i][j][k]=(ans[i][j][k-1]+1ll*jc[k]*f[k])%mod;
}
}
int q=read();
while(q--){
int l=read(),r=read(),k=read();
print(ans[l][r][k]),putchar('\n');
}
return 0;
}
快踩