2023.7.5

A

排队打水,\(n\) 个人,\(m\) 个水龙头,最小化总时间。

显然是一个 trival 的贪心。

#include<bits/stdc++.h>
#define N 1010
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
using namespace std;
int n,m,t[N];
priority_queue<pii>q;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		cin>>t[i];
	sort(t+1,t+1+n);
	for(int i=1;i<=m;i++)
		q.push(mp(0,i));
	int cur=0,ans=0;
	while(cur!=n){
		pii now=q.top();q.pop();
		ans+=-now.fi,cur++;
		q.push(mp(now.fi-t[cur],now.se));
	}
	while(!q.empty())
		ans+=-q.top().fi,q.pop();
	printf("%d\n",ans);
	
	return 0;
}

B

[ABC193E] Oversleeping

以下定义均在整数域上。

求非负整数 \(n,m\) 使得 \(\lbrack (2x+2y)\cdot n,(2x+2y)\cdot n+x)\)\(\lbrack (p+q)m+p,(p+q)\cdot (m+1))\) 有交集,最小化交集左端点的大小。

制約

  • 入力は全て整数
  • $ 1\ <\ =\ T\ <\ =\ 10 $
  • $ 1\ <\ =\ X\ <\ =\ 10^9 $
  • $ 1\ <\ =\ Y\ <\ =\ 500 $
  • $ 1\ <\ =\ P\ <\ =\ 10^9 $
  • $ 1\ <\ =\ Q\ <\ =\ 500 $

由于 \(y,q\) 很小,考虑枚举交点,即

\[(2x+2y)\cdot n+i=(p+q)\cdot m+j \]

解出 \(n\) 即可。\(n\) 要取模!

AT的评测机把我卡CE了,放一开始AC的码。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll T,x,y,p,q;
ll a,b,A,B,C,gcd;
ll n,m;
void exgcd(ll x,ll y){
	if(!y){a=1,b=0;return;}
	exgcd(y,x%y);
	ll t=a;
	a=b,b=t-x/y*b;
}
int main(){
	cin>>T;
	while(T--){
		cin>>x>>y>>p>>q;
		A=2*(x+y),B=p+q,gcd=__gcd(A,B);
		A/=gcd,B/=gcd,exgcd(A,B),a=(a%B+B)%B;
		ll ans=LONG_LONG_MAX;
		for(ll i=x;i<x+y;i++)
			for(ll j=p;j<p+q;j++){
				if((C=j-i)%gcd)continue;
				C/=gcd,n=(a*C%B+B)%B;
				ans=min(ans,(2*x+2*y)*n+i);
			}
		if(ans==LONG_LONG_MAX)puts("infinity");
		else printf("%lld\n",ans);
	}
	
	return 0;
}

C

XOR Tree

有人赛时AC?

带边权的树,一次可以将一条路径上的边全部异或一个值,求所有边为 \(0\) 的最小次数。

\(2\le n\le 10^5\)\(0\le a_i\le 15\).

一个很妙的想法:令一个点的边权为与它相连的边的异或和,那么边全为 \(0\rightarrow\) 点全为 \(0\).

取一条链异或即异或两个点,先把相等的消掉,因为值域是 \(\lbrack 1,15\rbrack\),再对剩下的做一个状压dp。

设当前状态为 \(S\),选取两个值 \(x,y\),转移到的状态即 \(S\operatorname{xor}x\operatorname{xor}y\operatorname{xor} (x\operatorname{xor}y)\).

\(x\operatorname{xor}y\in S\):消耗步数为 \(2\).

\(x\operatorname{xor}y\not\in S\):消耗步数为 \(1\).

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define R 16
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,val[N],cnt[R];
int S,f[1<<R],ans;
int main(){
	n=read();
	for(int i=1,u,v,w;i<n;i++){
		u=read()+1,v=read()+1,w=read();
		val[u]^=w,val[v]^=w;
	}
	for(int i=1;i<=n;i++)
		cnt[val[i]]++;
	for(int i=1;i<R;i++){
		ans+=cnt[i]/2;
		S|=((cnt[i]&1)<<i-1);
	}
	for(int i=0;i<(1<<R);i++)f[i]=1e9;
	f[S]=ans;
	for(int i=S;i;i--)
		for(int j=1;j<R;j++)
			for(int k=1;k<16;k++){
				if((i&(1<<j-1))&&(i&(1<<k-1))&&j!=k){
					int state=i^(1<<j-1)^(1<<k-1)^(1<<(j^k)-1);
					if(i&(1<<(j^k)-1))f[state]=min(f[state],f[i]+2);
					else f[state]=min(f[state],f[i]+1);
				}
			}
	printf("%d\n",f[0]);
	
	return 0;
}

D

\(h\times w\) 的棋盘中放若干个棋子,棋子可以攻击到同一行内距离不超过 \(d\) 的棋子。

问总方案数。答案对 \(100003\) 取模。

值域 \(1e9\).

先从dp入手:记 \(f(n)\) 为第 \(n\) 个格子最后放的方案数。

\[f(n)=\sum_{i=0}^{\max(0,n-d-1)}f(i) \]

边界值 \(f(0)=1\).

作前缀和 \(s\)

\[s_n=\begin{cases}n+1 & n\in\lbrack0,d\rbrack \\ s_{n-1}+s_{n-d-1} & x>d \end{cases}\]

答案即 \(s_n^h-1\).

使用矩阵快速幂可以做到时间复杂度 \(O(d^3\log w)\).

还有一种比较暴力的思想是直接枚举放多少个棋子。

若放了 \(m\) 个,前 \(m-1\) 次放就相当于吃掉了 \(d\) 个空位,所以方案数为 \(\displaystyle\binom{w-d(m-1)}{m}\).

套用卢卡斯定理可以做到时间复杂度 \(\displaystyle O(\frac{w}{d}\cdot\log_{p}w)\).

我们设一个阈值 \(B\),数据分治即可。

#include<bits/stdc++.h>
#define P 100003
#define B 50
using namespace std;
int qpow(int k,int b){
	int ret=1;k%=P;
	while(b){
		if(b&1)ret=1ll*ret*k%P;
		k=1ll*k*k%P,b>>=1;
	}
	return ret;
}
int d,h,w;
int fac[P],inv[P];
void init1(){
	fac[0]=inv[0]=1;
	for(int i=1;i<P;i++)
		fac[i]=1ll*fac[i-1]*i%P;
	inv[P-1]=qpow(fac[P-1],P-2);
	for(int i=P-2;i;i--)
		inv[i]=1ll*inv[i+1]*(i+1)%P;
}
int C(int n,int m){
	if(n<0||m<0||n<m)return 0;
	if(!n||!m)return 1;
	return 1ll*fac[n]*inv[n-m]%P*inv[m]%P;
}
int Lucas(int n,int m){
	if(n<0||m<0||n<m)return 0;
	if(!n||!m)return 1;
	return 1ll*C(n%P,m%P)*Lucas(n/P,m/P)%P;
}
void solve1(){
	int ans=0;init1();
	for(int i=0,tp;w-d*(i-1)>=0;i++){
		tp=Lucas(w-d*(i-1),i);
		(ans+=tp)%=P;
	}
	printf("%d\n",(qpow(ans,h)+P-1)%P);
}
struct Mat{
	int a[B+5][B+5];
	Mat(){memset(a,0,sizeof(a));}
};
Mat NoOperatorDefinedIsNoob(Mat x,Mat y){
	Mat ret;
	for(int i=0;i<=d;i++)
		for(int k=0;k<=d;k++)
			for(int j=0;j<=d;j++)
				(ret.a[i][j]+=1ll*x.a[i][k]*y.a[k][j]%P)%=P;
	return ret; 
}
Mat Ans,base;
void init2(){
	base.a[0][0]=base.a[d][0]=1;
	for(int i=0;i<d;i++)
		base.a[i][i+1]=1;
	for(int i=0;i<=d;i++)
		Ans.a[0][i]=d-i+1;
}
void MatPow(int b){
	while(b){
		if(b&1)Ans=NoOperatorDefinedIsNoob(Ans,base);
		base=NoOperatorDefinedIsNoob(base,base),b>>=1;
	}
}
void solve2(){
	if(w<=d){
		printf("%d\n",(qpow(w+1,h)+P-1)%P);
		return;
	}
	init2(),MatPow(w-d);
	printf("%d\n",(qpow(Ans.a[0][0],h)+P-1)%P);
}
int main(){
	cin>>d>>h>>w;
	if(d>=B)solve1();
	else solve2();
	
	return 0;
}
posted @ 2023-08-06 20:07  SError  阅读(16)  评论(0编辑  收藏  举报