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
以下定义均在整数域上。
求非负整数 \(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\) 很小,考虑枚举交点,即
解出 \(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
有人赛时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(0)=1\).
作前缀和 \(s\):
答案即 \(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;
}