A. Potion-making
要达成一定的配比,考虑最坏情况k和100-k,然后分别除以最小公因子的和便是答案(2min)
void solve(){
cin>>k;
int x=k,sum=100;
sum-=x;
int d=gcd(x,sum);
print(x/d+sum/d);
}
直接分类讨论即可,最大值最小值的位置,如果有序是0,如果最大值或者最小值在自己的位置上是1,如果都不在但是最大值和最小值都在中间是2,否则如果最大值在1最小值在n位3(3min)
void solve(){
n=read();rep(i,1,n) a[i]=read();
if(check()) print(0);
else if(n==a[n]||1==a[1]) print(1);
else if(n==a[1]&&1==a[n]) print(3);
else print(2);
}
C. Robot Collisions
机器人向左或者向右,距离位偶数的一定会相遇,但是遇到第一个会爆炸。这道题和USACO的一道题很像,用栈来模拟,因为距离有奇偶数影响,所以分别存一个奇数栈和偶数栈,然后先排序然后顺序扫描的时候所有R入栈,然后L和最近的R匹配。如果栈空则L入栈,这样说明了一段时间后其会变成对应的最小的向右元素。全部结束后如果栈还未空则两两配对。
思维难度有一点,写起来也有细节 (40min)
#define int LL
const int N=300010,M=N*2,mod=1e9+7;
int n,m,k;//,a[N],b[N];
string p;
struct Rob{
int x,v,id;
}a[N];
stack<Rob> J,O;
int ans[N];
int calc(Rob A,Rob B){
if(A.v==B.v){
if(A.v==1) return (m-A.x+m-B.x)/2;
else return (A.x+B.x)/2;
}
else{
if(A.x>B.x) swap(A,B);
if(A.v==1) return (B.x-A.x)/2;
else return (m-B.x+A.x+m)/2;
}
}
void solve(){
while(J.size()) J.pop(); while(O.size()) O.pop();
n=read(),m=read();
rep(i,1,n) ans[i]=-1;
rep(i,1,n) a[i].x=read(),a[i].id=i;
rep(i,1,n){
char op[2];
scanf("%s",op);
if(*op=='L') a[i].v=-1;else a[i].v=1;
}
sort(a+1,a+1+n,[](Rob &A,Rob &B){
return A.x < B.x;
});
for(int i=1;i<=n;++i){
if(a[i].v==1){
a[i].x%2==0?O.push({a[i].x,a[i].v,a[i].id}):J.push({a[i].x, a[i].v,a[i].id});
}
else
if(a[i].x%2==0){
if(O.size()) ans[O.top().id]=ans[a[i].id]=calc(O.top(), a[i]),O.pop();
else O.push(a[i]);
}
else{
if(J.size()) ans[J.top().id]=ans[a[i].id]=calc(J.top(), a[i]),J.pop();
else J.push(a[i]);
}
}
while(J.size()>1) {
auto a=J.top();J.pop();
auto b=J.top();J.pop();
ans[a.id]=ans[b.id]=calc(a,b);
}while(O.size()>1) {
auto a=O.top();O.pop();
auto b=O.top();O.pop();
ans[a.id]=ans[b.id]=calc(a,b);
}
rep(i,1,n) printf("%d ",ans[i]);
puts("");
}
D. Armchairs
怀疑和C弄反了…这道题我先贪心的WA了一发,后来发现不管数据范围真的很蠢。看到数据范围就5000一定是一个二维的DP,然后就想到了先把所有有人的位置搞出来,然后f[i][j]
表示前i个有人的位置在j个位置里变换,没有一个原来有人的位置变换后有人的最小代价(15min)
状态转移方程:
f[i][j] = f[i-1][j];
if(!st[i]) f[i][j]=min(f[i][j], f[i-1][j-1]+abs(j-b[i])
直接看代码:
#define int LL
const int N=5010,M=N*2,mod=1e9+7;
int n,m,k,a[N],b[N],idx,f[N][N];
bool st[N];
void solve(){
memset(f,0x3f,sizeof f);
n=read();
rep(i,1,n) {st[i]=read();if(st[i]) b[++idx]=i;}
rep(i,0,n) f[0][i]=0;
for(int i=1;i<=idx;++i){
for(int j=1;j<=n;++j){
f[i][j]=f[i][j-1];
if(!st[j])
f[i][j]=min(f[i][j], f[i-1][j-1]+abs(j-b[i]));
}
}
int ans=mod;
for(int i=1;i<=n;++i) ans=min(ans, f[idx][i]);
print(ans);
}
E. Assimilation IV (补题)
大意就是n个城市m个点,每个城市和点之间都有距离。然后距离<=n+1,第i轮随机选择一个城市占领所有距离城市<=i的点,问占领的点的数量的期望
首先我们并不好直接求哪些城市被占领了,被占领的权重又是多少,因为这样需要枚举所有状态数(NP
因此我们考虑每一轮选点有哪些点不能被选到,这样逆向减去就是最终答案了。
学了一种比较巧妙地写法:
const int N=200010,M=N*2,mod=998244353;
int n,m,dist[N][25],fact;
/*算出不能控制的纪念碑的期望,然后总数减去便是答案*/
void solve(){
fact=1;
n=read(),m=read();
for(int i=1,x;i<=n;++i){
fact=1ll*fact*i%mod; //所有情况数
rep(j,1,m) x=read(),dist[j][x]++; //统计距离每个点x长度的数量
}
int ans=0;
for(int i=1;i<=m;++i){ //依次计算分子
int tmp=1,tot=0;
for(int j=n;j;--j){ //从第n个操作开始枚举
tot += dist[i][j+1];
tmp=1ll*tmp*tot%mod;
tot -= 1; //被选择了一个点
}
ans=(1ll*fact-tmp+mod+ans)%mod;
}
ans=1ll*ans*fpower(fact,mod-2,mod)%mod;
print(ans);
}