3.7
E. Arena 2100
https://codeforces.com/problemset/problem/1606/E
题意见洛谷。
n,x<=500
题解:显然是一道dp,考虑状态:需要能够随转移变化的状态,而转移显然是生命值的变化和人数的变化,什么生命值最能表征当前状态?最大生命值。故令f[i][j]表示总人数为i,最大生命值为j时的可行方案数。
当j<=i-1时,显然下一步全死,故f[i][j]=ji-(j-1)i;
ELSE f[i][j]=∑(f[k][j-i+1]C(i,k)(i-1)^(i-k));(注意人是不同的)
代码:
#define int long long
using namespace std;
const int N=605,mod=998244353;
int f[N][N],fac[N],finv[N],c[605][605];
int qpow(int x,int y){
int ans=1;
while(y){
if(y&1) ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
void init(){
fac[0]=1;
for(int i=1;i<=505;i++){
fac[i]=fac[i-1]*i%mod;
}
for(int i=0;i<=505;i++){
finv[i]=qpow(fac[i],mod-2);
}
}
int mo(int x){
x=x%mod;
if(x<0) x+=mod;
if(x>mod) x-=mod;
return x;
}
int C(int n,int m){
if(m==0) return 1;
return fac[n]*finv[m]%mod*finv[n-m]%mod;
}
signed main(){
int n,x;cin>>n>>x;
init();
for(int i=2;i<=n;i++){
for(int j=1;j<=x;j++){
if(j<=i-1) f[i][j]=((qpow(j,i)-qpow(j-1,i))%mod+mod)%mod;
else{
int& w=f[i][j];
for(int k=1;k<=i;k++){
w=(w+C(i,k)*f[k][j-i+1]%mod*qpow(i-1,i-k)%mod)%mod;
}
}
}
}
int ans=0;
for(int i=1;i<=x;i++){
ans=(ans+f[n][i])%mod;
}
cout<<ans;
}
New Game Plus! 2200
https://www.luogu.com.cn/problem/CF1415E
题解:观察一波,好像一个dp,一看数据,好像不太行,考虑贪心,而根据数据猜测是一个nlogn解法,可能是用堆。
正经分析开始:其给的k个归零操作实质上是把其分为k+1个组,贪心地,每个组元素不增。当放入一个元素时,获取该组当前地值f[i],f[i]->f[i]+a[k],易知用最大的那一组是最优的,故将组放入堆中,每次取top即可。
代码:
#define int long long
using namespace std;
const int N=5e5+100,INF=1e18;
int n,k;
int a[N],b[N],c[N],cnt[N];
priority_queue<int> q;
signed main(){
cin>>n>>k;
int w=n+1,tot=0,ans=0;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for(int i=1;i<=n;i++) b[i]=a[n-i+1];
for(int i=1;i<=k+1;i++) q.push(0);
for(int i=1;i<=n;i++){
int x=q.top();q.pop();
ans+=x;
q.push(x+b[i]);
}
cout<<ans;
}
Present 2100
https://www.luogu.com.cn/problem/CF1322B
题解:对于异或我们可以按位处理。难点在于如何处理进位,我们发现,对于第k位,只有前k位对其值有影响,我们可以将每一个数取模至前k位,对于任意两数x,y,相加后第k位为1当且仅当2k<=x+y<2(k+1)||2k+2^(k+1)<=x+y,故我们可以对取模过的数排序后倒序枚举,用双指针找到有多少个1,即可确定该位为0还是1,算法复杂度为O(nlognlogN).
代码:
#define int long long
using namespace std;
const int N=4e5+100,INF=1e18;
int n;
int a[N],b[N];
int tp(int x,int y){
int l=1,r=1,ans=0;
for(int i=n;i>=1;i--){
while(l<=n&&b[l]+b[i]<x) l++;
while(r<=n&&b[r]+b[i]<y) r++;
if(l>n) break;
if(r>n) r--,ans++;
int ww=(l<=i&&r>i);
ans+=(r-l)-ww;
}
return ans/2;
}
signed main(){
cin>>n;
int res=0;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int k=25;
for(int p=0;p<=k;p++){
int q=(1<<p),w=(1<<(p+1)),e=((1<<(p+1))+(1<<p));
for(int i=1;i<=n;i++) b[i]=a[i]%w;
sort(b+1,b+n+1);
int ans=tp(q,w)+tp(e,(1<<(p+2))-1);
if(ans%2) res+=(1<<p);
}
cout<<res;
}
Missing Numbers 1900
https://www.luogu.com.cn/problem/CF1081E
题解:贪心地想,想要使得前2i项和为平方数, 由于x[2i]给定,故我们希望前2i-1项和尽量小.对于2i项,x[2i]=AA-BB=(A-B)(A+B)<=2e5,故我们可以O(sqrt(n))得到A和B的可能值,故我们可以O(nsqrt(n))得到可能的解。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+100;
int a[N],s[N];
signed main(){
int n;cin>>n;
for(int i=1;i<=n/2;i++){
cin>>a[i*2];
}
for(int i=2;i<=n;i+=2){
int x=a[i];
for(int j=1;j*j<=x;j++){
if(x%j!=0) continue;
int u,v;
u=(j+x/j)/2,v=(x/j-j)/2;
if((j+x/j)%2||(x/j-j)%2) continue;
if(v*v<=s[i-2]) continue;
a[i-1]=v*v-s[i-2];
s[i]=s[i-2]+a[i-1]+a[i];
}
if(!a[i-1]){
cout<<"No";return 0;
}
}
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}