互测2


A. 游行

发现可以转化成最小点覆盖,每条路径不覆盖起点

没有覆盖的点产生 \(C\) 的贡献

于是两点之间的边权连最短路,用 \(EK\) 跑费用流

跑出覆盖 \(i\) 个点的最小代价

然后询问的时候可以二分也可以直接扫一遍,找出最小的答案

Code
#include
#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,Q,ans,C,S,T,now;
int head[510],ver[130000],cost[130000],to[130000],edge[130000],tot=1;
int f[510][510];
int c[510],dis[510],mf[510],pre[510],q[130000],h,t;
bool inq[510];
inline void add(int x,int y,int z,int w){
	ver[++tot]=y;edge[tot]=z;cost[tot]= w;to[tot]=head[x];head[x]=tot;
	ver[++tot]=x;edge[tot]=0;cost[tot]=-w;to[tot]=head[y];head[y]=tot;
}
inline bool spfa(){
	for(int i=1;i<=T;i++) dis[i]=inf,inq[i]=0;
	dis[S]=0;q[h=t=1]=S;mf[S]=inf;inq[S]=1;
	while(h<=t){
		int x=q[h++];inq[x]=0;
		for(int i=head[x];i;i=to[i]) if(edge[i]){
			int y=ver[i];
			if(dis[y]>dis[x]+cost[i]){
				dis[y]=dis[x]+cost[i];
				mf[y]=min(mf[x],edge[i]);pre[y]=i;
				if(!inq[y]) inq[q[++t]=y]=1;
			}
		}
	}
	return dis[T]!=inf;
}
inline void ek(){
	int x=T;
	while(x!=S){
		int i=pre[x];
		edge[i]-=mf[T];edge[i^1]+=mf[T];x=ver[i^1];
	}
	now++;c[now]=c[now-1]+mf[T]*dis[T];
}
inline void solve(){
	ans=inf;for(int i=0;i<=now;i++) ans=min(ans,c[i]+C*(n-i));
	printf("%lld\n",ans);
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	n=read(),m=read(),Q=read();memset(f,0x3f,sizeof(f));S=n+n+1,T=n+n+2;
	for(int i=1,x,y,z;i<=m;i++){x=read(),y=read(),z=read();f[x][y]=min(f[x][y],z);}
	for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
	for(int i=1;i<=n;i++) add(S,i,1,0),add(i+n,T,1,0);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j) add(i,j+n,1,f[i][j]);
	while(spfa()) ek();
	for(int i=1,l,r;i<=Q;i++){C=read();solve();}
	return 0;
}

B. 暴力题

推式子懒得写,直接给出最后的(默认 \(n

\(\sum\limits_{T=1}^n\sum\limits_{d|T}\frac{d}{\varphi(d)}\mu(\frac{T}{d})\sum\limits_{i=1}^{n/T}\varphi(iT)\sum\limits_{j=1}^{m/T}\varphi(jT)\)

预处理出所有的 \(\sum\limits_{d|T}\frac{d}{\varphi(d)}\)\(\sum\limits_{i=1}^{m/T}\varphi(iT)\)

在处理出 \(\sum\limits_{T=1}^x\sum\limits_{d|T}\frac{d}{\varphi(d)}\mu(\frac{T}{d})\sum\limits_{i=1}^{y}\varphi(iT)\sum\limits_{j=1}^{z}\varphi(jT)\)

然后对于每次询问把 \(T<\frac{m}{B}\) 的暴力算,剩下的整数分块算

Code
#include
#define int long long
#define rint signed
#define mod 998244353
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline 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<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int T,n,m;
int f[100010],s[42][42][100010],inv[100010];
vector g[100010];
int prime[100010],phi[100010],mu[100010],cnt;
bool is[100010];
inline void md(int x){x=(x>=mod)?x-mod:x;}
inline int qpow(int x,int k){
	int res=1,base=x;
	while(k){if(k&1) res=res*base%mod;base=base*base%mod;k>>=1;}
	return res;
}
inline int G(int x,int y){
	if(x<=40) return g[x][y];
	int res=0;for(int i=1;i<=y;i++) md(res+=phi[x*i]);return res;
}
inline void solve(){
	n=read(),m=read();if(n>m) swap(n,m);
	int res=0;
	for(int i=1;i<=m/40;i++) (res+=f[i]*g[i][n/i]%mod*g[i][m/i])%=mod;
	for(int l=m/40+1,r;l<=n;l=r+1){
		r=min(n/(n/l),m/(m/l));
		(res+=(s[n/l][m/l][r]-s[n/l][m/l][l-1]+mod))%=mod;
	}
	printf("%lld\n",res);
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	phi[1]=mu[1]=1;
	for(int i=2;i<=100000;i++){
		if(!is[i]) prime[++cnt]=i,mu[i]=-1,phi[i]=i-1;
		for(int j=1;j<=cnt&&i*prime[j]<=100000;j++){
			is[i*prime[j]]=1;
			if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];break;}
			mu[i*prime[j]]=-mu[i];phi[i*prime[j]]=phi[i]*phi[prime[j]];
		}
	}
	for(int i=1;i<=100000;i++) inv[i]=qpow(i,mod-2);
	for(int i=1;i<=100000;i++) for(int j=1;i*j<=100000;j++) f[i*j]+=i*inv[phi[i]]%mod*mu[j]%mod;
	for(int i=1;i<=100000;i++) md(f[i]=f[i]%mod+mod);
	for(int i=1;i<=100000;i++){
		g[i].emplace_back(0);
		for(int j=1;i*j<=100000;j++){
			g[i].emplace_back(0);
			g[i][j]=g[i][j-1]+phi[i*j];
		}
	}
	for(int i=1;i<=40;i++) for(int j=1;j<=40;j++) for(int k=1;k<=100000&&k*i<=100000&&k*j<=100000;k++) (s[i][j][k]=s[i][j][k-1]+f[k]*g[k][i]%mod*g[k][j])%=mod;
	T=read();while(T--) solve();
	return 0;
}

C. 卡常题

分块搞一搞卡卡常就行, \(O(n\sqrt{n}\log n)\) 可过

正解也好写,提前对每块排序,然后直接归并算序列间的逆序对数就行

就是把树状数组替换掉

答案要开 \(long long\)

Code
#include
//#define int long long
#define rint signed
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,m,typ;
long long lst;
int a[100001],b[100001],bl[100001];
int BIT[100001];
int le[646],ri[646];
int sum[100001][646],sum2[100001][646],bsum[646][646],S[646];
bool vis[100001];
inline bool cmp1(int x,int y){return a[x]>a[y];}
inline bool cmp2(int x,int y){return a[x]>1;
	if(pos<=mid){for(int i=pos;i>=le[bl[pos]];i--) res+=vis[i];}
	else{res=S[bl[pos]];for(int i=pos;i<=ri[bl[pos]];i++) res-=vis[i];}
	vis[pos]=1;S[bl[pos]]++;sum[pos][bl[pos]]=res;bsum[bl[pos]][bl[pos]]+=res;
	for(int i=bl[pos]-1;i;i--){res+=S[i];sum[pos][i]=res;bsum[bl[pos]][i]+=res;}
}
void insc(int pos){
	int res=0,mid=(le[bl[pos]]+ri[bl[pos]])>>1;
	if(pos<=mid){res=S[bl[pos]];for(int i=pos;i>=le[bl[pos]];i--) res-=vis[i];}
	else{for(int i=pos;i<=ri[bl[pos]];i++) res+=vis[i];}
	vis[pos]=1;S[bl[pos]]++;sum2[pos][bl[pos]]=res;
	for(int i=bl[pos]+1;i<=bl[n];i++){res+=S[i];sum2[pos][i]=res;}
}
long long queryb(int l,int r){
	long long res=0;
	if(bl[l]==bl[r]){
		for(int i=l;i<=r;i++){res+=query(a[i]);ins(a[i]);}
		for(int i=l;i<=r;i++){del(a[i]);}
		return res;
	}
	for(int i=r;i>=le[bl[r]];i--) res+=sum[i][bl[l]+1];
	for(int i=l;i<=ri[bl[l]];i++) res+=sum2[i][bl[r]-1];
	for(int i=bl[l]+1;i<=bl[r]-1;i++) res+=bsum[i][bl[l]+1];
	for(int i=l;i<=ri[bl[l]];i++) ins(a[i]);
	for(int i=le[bl[r]];i<=r;i++) res+=query(a[i]);
	for(int i=l;i<=ri[bl[l]];i++) del(a[i]);
	return res;
}
signed main(){
#ifdef LOCAL
	freopen("in","r",stdin);
	freopen("out","w",stdout);
#endif
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	n=read(),m=read(),typ=read();
	for(int i=1;i<=n;i++) a[i]=read(),bl[i]=(i-1)/155+1;
	for(int i=1;i<=bl[n];i++) le[i]=(i-1)*155+1,ri[i]=min(155*i,n);
	for(int i=1;i<=n;i++) b[i]=i;sort(b+1,b+1+n,cmp1);for(int i=1;i<=n;i++) insb(b[i]);
	memset(vis,0,sizeof(vis));memset(S,0,sizeof(S));
	sort(b+1,b+1+n,cmp2);for(int i=1;i<=n;i++) insc(b[i]);
	for(int i=1,l,r;i<=m;i++){
		l=read()^(lst*typ),r=read()^(lst*typ);
		printf("%lld\n",lst=queryb(l,r));
	}
	return 0;
}