2020 省选联考
A 卷 T1/B 卷 T3 冰火战士
解析:撇开花里胡哨的比赛方式,首先容易看出每次比赛的消耗的能量为当时能量和小的那一边的两倍,这是因为车轮战中,两边的能量消耗一定是相同的,而能量和小的那一方会先被打败。
由于冰系战士要求场地温度大于等于他的自身温度才能参赛,因此当场地温度变大时,冰系战士参赛的人也会越多,即冰系战士的总能量随着场地温度变大而递增。同理可得,火系战士的总能量随着场地温度变大而递减。如下图所示:

取
这下就很好处理了,我们先离散化,然后用树状数组维护
我们考虑直接在树状数组中二分,从大到小枚举
int now=0,sum=0;
for(int i=20;i>=-;i--)
{
int nex=now|(1<<i);
if(nex<=n&&sum+...)//后面为二分要的条件
{
sum+=...;
now|=(1<<i);
}
}
然后这道题就解决了。
/*
* @Author: clorf
* @Date: 2021-04-04 11:21:53
* @Last Modified by: clorf
* @Last Modified time: 2021-04-04 22:08:16
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cctype>
#define INF 2e9
#define rg register
using namespace std;
const int maxn=2e6;
const long double Pi=acos(-1.0);
const double eps=1e-7;
//1.数组开小
//2.没用long long/爆了类型存储范围
//3.写之前式子没推清楚
//4.变量写错(想当然typo/没想清楚含义)
//5.写之前没想清楚复杂度
//6.常量数字与long long连用时要加ll!!!
//7.考虑无解和多解的情况
//8.两个int连成爆long long的一定要加1ll!!!!
//9.先除后乘!!!!!!
template<class T>void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}
int n,tot;
int op[maxn+5],type[maxn+5];
int x[maxn+5],y[maxn+5];
int b[maxn+5],f[maxn+5];
inline int lowbit(int x)
{
return x&(-x);
}
struct BIT
{
int tr[maxn+5],sum,cnt;
inline void add(int x,int val)
{
for(int i=x;i<=tot;i+=lowbit(i))
tr[i]+=val;
}
inline int query(int x)
{
int num=0;
for(int i=x;i;i-=lowbit(i))
num+=tr[i];
return num;
}
}ice,fire;
inline int get(int x)
{
if(x<1||x>tot)
return -1;
return min(ice.query(x),fire.sum-fire.query(x-1));
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&op[i],&type[i]);
if(op[i]==1)
{
scanf("%d%d",&x[i],&y[i]);
b[++tot]=x[i];
}
}
sort(b+1,b+tot+1);
tot=unique(b+1,b+tot+1)-b-1;
for(int i=1;i<=n;i++)
if(op[i]==1)
x[i]=lower_bound(b+1,b+tot+1,x[i])-b;
for(int i=1;i<=n;i++)
{
if(op[i]==1)
{
if(!type[i])
{
ice.add(x[i],y[i]);
ice.cnt++;
ice.sum+=y[i];
}
else
{
fire.add(x[i],y[i]);
fire.cnt++;
fire.sum+=y[i];
f[x[i]]+=y[i];
}
}
else
{
int now=type[i];
if(!type[now])
{
ice.add(x[now],-y[now]);
ice.cnt--;
ice.sum-=y[now];
}
else
{
fire.add(x[now],-y[now]);
fire.cnt--;
fire.sum-=y[now];
f[x[now]]-=y[now];
}
}
if(!(ice.cnt&&fire.cnt))
{
puts("Peace");
continue;
}
int now=0,sum1=0,sum2=0;
for(int i=20;i>=0;i--)
{
int nex=now|(1<<i);
if(nex<=tot&&sum1+ice.tr[nex]<=fire.sum-(sum2+fire.tr[nex])+f[nex])
{
now=nex;
sum1+=ice.tr[nex];
sum2+=fire.tr[nex];
}
}
int ans1=get(now),ans2=get(now+1);
if(ans1<=0&&ans2<=0)
{
puts("Peace");
continue;
}
if(ans1>ans2)
{
printf("%d %d\n",b[now],ans1<<1);
continue;
}
now=sum2=0;
for(int i=20;i>=0;i--)
{
int nex=now|(1<<i);
if(nex<=tot&&fire.sum-(sum2+fire.tr[nex])+f[nex]>=ans2)
{
now=nex;
sum2+=fire.tr[nex];
}
}
printf("%d %d\n",b[now],ans2<<1);
}
return 0;
}
A 卷 T2 组合数问题
解析:先推一波式子:
然后普通幂转下降幂,这个需要用到斯特林反演,公式如下:
这个式子
设转化后
所以得到
然后用这个式子化简:
然后用吸收公式,即:
可得:
内层和式转为枚举
然后就可以
/*
* @Author: clorf
* @Date: 2021-04-04 22:08:35
* @Last Modified by: clorf
* @Last Modified time: 2021-04-05 17:53:28
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cctype>
#define INF 2e9
#define rg register
using namespace std;
const int maxm=1000;
const long double Pi=acos(-1.0);
const double eps=1e-7;
//1.数组开小
//2.没用long long/爆了类型存储范围
//3.写之前式子没推清楚
//4.变量写错(想当然typo/没想清楚含义)
//5.写之前没想清楚复杂度
//6.常量数字与long long连用时要加ll!!!
//7.考虑无解和多解的情况
//8.两个int连成爆long long的一定要加1ll!!!!
//9.先除后乘!!!!!!
template<class T>void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}
int n,x,p,m,ans;
int d[maxm+5];
int a[maxm+5],b[maxm+5];
int S[maxm+5][maxm+5];
inline int inc(int a,int b)
{
return a+b>=p?a+b-p:a+b;
}
inline int dec(int a,int b)
{
return a-b<0?a-b+p:a-b;
}
inline int power(int a,int b)
{
int ans=1;
for(;b;b>>=1)
{
if(b&1)
ans=1ll*ans*a%p;
a=1ll*a*a%p;
}
return ans;
}
int main()
{
scanf("%d%d%d%d",&n,&x,&p,&m);
for(int i=0;i<=m;i++)
scanf("%d",&a[i]);
S[0][0]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=i;j++)
S[i][j]=inc(S[i-1][j-1],1ll*j*S[i-1][j]%p);
for(int i=0;i<=m;i++)
for(int j=i;j<=m;j++)
b[i]=inc(b[i],1ll*S[j][i]*a[j]%p);
d[0]=1;
for(int i=1;i<=m;i++)
d[i]=1ll*d[i-1]*(n-i+1)%p;
for(int i=0;i<=m;i++)
ans=inc(ans,1ll*b[i]*d[i]%p*power(x,i)%p*power(x+1,n-i)%p);
printf("%d",ans);
return 0;
}
A 卷 T3 魔法商店
不会保序回归,爪巴
A 卷 T4 信号传递
解析:首先对于传递序列上相邻的两个信号站
设
设
枚举
考虑优化,我们对于每个集合
现在思考怎么预处理
其实就是把原来的
预处理和 DP 的时间复杂度都为
我们考虑 DP 和
所以总空间复杂度为
/*
* @Author: clorf
* @Date: 2021-04-05 20:24:50
* @Last Modified by: clorf
* @Last Modified time: 2021-04-05 21:49:21
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<ctime>
#include<cctype>
#include<queue>
#define INF 2e9
#define rg register
using namespace std;
const int maxn=1e5,maxm=23;
const int maxk=100,maxlim=1<<23;
const long double Pi=acos(-1.0);
const double eps=1e-7;
//1.数组开小
//2.没用long long/爆了类型存储范围
//3.写之前式子没推清楚
//4.变量写错(想当然typo/没想清楚含义)
//5.写之前没想清楚复杂度
//6.常量数字与long long连用时要加ll!!!
//7.考虑无解和多解的情况
//8.两个int连成爆long long的一定要加1ll!!!!
//9.先除后乘!!!!!!
template<class T>void read(T &x)
{
x=0;int f=0;char ch=getchar();
while(!isdigit(ch)){f|=(ch=='-');ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return;
}
int n,m,k,f[maxlim+5];
int s[maxn+5],cnt[maxlim+5];
int num[maxm+5][maxm+5];
int pos[maxlim+5];
struct state
{
int now;
int cost[maxm+5];
};
queue<state> q;
inline int lowbit(int x)
{
return x&(-x);
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i]);
s[i]--;
}
for(int i=1;i<n;i++)
num[s[i]][s[i+1]]++;
int lim=(1<<m)-1;
for(int i=1;i<=lim;i++)
{
cnt[i]=cnt[i>>1]+(i&1);
f[i]=INF;
}
state st;
st.now=0;
for(int i=0;i<m;i++)
{
st.cost[i]=0;
pos[1<<i]=i;
for(int j=0;j<m;j++)
{
if(j==i)
continue;
st.cost[i]-=num[i][j];
st.cost[i]+=k*num[j][i];
}
}
q.push(st);
while(!q.empty())
{
state u=q.front();
q.pop();
int now=u.now;
int nowp=cnt[now]+1;
for(int T=lim^now;T;T-=lowbit(T))
{
int i=pos[lowbit(T)];
f[now|(1<<i)]=min(f[now|(1<<i)],f[now]+nowp*u.cost[i]);
}
for(int i=0;i<m;i++)
{
if((now>>i)&1)
break;
state v;
v.now=now|(1<<i);
for(int T=lim^v.now;T;T-=lowbit(T))
{
int j=pos[lowbit(T)];
v.cost[j]=u.cost[j]+(k*num[j][i]+num[i][j])-(k*num[i][j]-num[j][i]);
}
q.push(v);
}
}
printf("%d",f[lim]);
return 0;
}

