2021.10.29考試總結[衝刺NOIP模擬18]
這題面屬實樂子
給我考成敗犬了
T1 莓良心
我打扮成你喜歡的樣子來看你了,廣,不,da,darling...
不難看出\(w\)值與它的貢獻次數無關。考慮每個方案它必定會有\(1\)的貢獻與跟其他元素結合產生的額外貢獻,有
\[ans=(\sum_{i=1}^nw_i)\times\begin{Bmatrix}n\\k\end{Bmatrix}+(\sum_{u!=v}(w_u+w_v))\begin{Bmatrix}n-1\\k\end{Bmatrix} \]\[ans=(\sum_{i=1}^nw_i)\times(\begin{Bmatrix}n\\k\end{Bmatrix}+(n-1)\begin{Bmatrix}n-1\\k\end{Bmatrix}) \]其中\(\begin{Bmatrix}n\\k\end{Bmatrix}\)
有
\[\begin{Bmatrix}n\\k\end{Bmatrix}=\frac{1}{k!}\sum_{i=0}^k(-1)^i\binom{k}{i}(k-i)^n \]線性篩出所有\(k-i\)的\(n\)次方和\(n-1\)次方。
\(code:\)
T1
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=1000010,mod=998244353;
int n,k,cnt,sum,tmp1,tmp2,f[NN],g[NN],pri[NN];
bool vis[NN];
namespace Combination{
int inv[NN],fac[NN];
int C(int x,int y){ return x<0||y<0||x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combination;
void get(){
f[1]=1; g[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]) pri[++cnt]=i,f[i]=qpow(i,n),g[i]=qpow(i,n-1);
for(int j=1;j<=cnt&&pri[j]*i<=n;i++){
vis[pri[j]*i]=1;
f[pri[j]*i]=f[pri[j]]*f[i]%mod;
g[pri[j]*i]=g[pri[j]]*g[i]%mod;
if(i%pri[j]==0) break;
}
}
}
signed main(){
freopen("ichigo.in","r",stdin);
freopen("ichigo.out","w",stdout);
n=read(); k=read(); init(); get();
for(int i=1;i<=n;i++) (sum+=read())%=mod;
for(int i=0;i<=k;i++){
(tmp1+=((i&1)?-1:1)*C(k,i)*f[k-i]%mod+mod)%=mod;
(tmp2+=((i&1)?-1:1)*C(k,i)*g[k-i]%mod+mod)%=mod;
}
(tmp1*=qpow(fac[k],mod-2))%=mod;
(tmp2*=qpow(fac[k],mod-2))%=mod;
sum=sum*(tmp1+(n-1)*tmp2%mod)%mod;
write(sum,'\n');
return 0;
}
T2 盡梨了
我成為你心中的第一位了嗎?
先考慮\(a_i>0\)的情況。
考慮臨項交換,對於\(i<j\),計算排隊多出的時間,i在\(j\)之前更優,則
\[(a_i\times t+b_i+1)\times a_j<(a_j\times t+b_j+1)\times a_i \]即
\[(b_i+1)\times a_j<(b_j+1)\times a_i \]按這種關係排序,則最終答案肯定由排序後的子序列產生。設狀態\(f_{i,j}\)為考慮到第\(i\)位,選了\(j\)個元素的最短時間。那麼有
\[f_{i,j}=min(f_{i-1,j},f_{i-1,j-1}+a_i\times(f_{i-1,j-1}+1)+b_i+1) \]因為\(a_i>0\)
\(DP\)之後考慮\(a_i=0\)的元素,從小到大選就行了。
\(code:\)
T2
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=200010;
int n,t,ext,ans,tim,dlt[NN],f[NN][50];
vector<int>zro;
struct shop{
int a,b;
bool operator<(const shop& x)const{
return (b+1)*x.a<(x.b+1)*a;
}
}s[NN];
signed main(){
freopen("eriri.in","r",stdin);
freopen("eriri.out","w",stdout);
n=read(); t=read();
for(int a,b,i=1;i<=n;i++){
a=read(); b=read();
if(!a) zro.push_back(b);
else s[++ext]=(shop){a,b};
}
sort(s+1,s+ext+1); sort(zro.begin(),zro.end());
memset(f,0x3f,sizeof(f)); f[0][0]=0;
for(int i=1;i<=ext;i++)
for(int j=0;j<50;j++){
f[i][j]=f[i-1][j];
if(j&&f[i-1][j-1]<=t)
ckmin(f[i][j],f[i-1][j-1]+1+s[i].b+(1+f[i-1][j-1])*s[i].a);
}
for(int i=0;i<50;i++) if(f[ext][i]<=t){
int k=f[ext][i],res=i;
for(int j:zro){
k+=j+1;
if(k>t) break;
++res;
}
ckmax(ans,res);
}
write(ans,'\n');
return 0;
}
T3 團不過
眼淚沒能流出來。因為已經哭過很多次了。
容斥。
令\(p_i\)表示\(i\)堆石子的總方案數,有
\[p_i=(2^n-1)^{\underline{i}} \]令\(f_i\)表示\(i\)堆石子先手必敗的方案數。
當前\(i-1\)堆石子異或和不為零時,若不考慮互不相同,那麼總可以用最後一個數調整至異或和為零。這部分方案為\(p_{i-1}-f_{i-1}\)。
接下來容斥掉有數相同的情況。當前\(i-1\)堆石子有\(i-2\)堆石子異或和為零時,另兩堆石子必定相等。這種情況數量為\((i-1)\times(2^n-i+1)\times f_{i-2}\)。位置乘數乘方案數。
綜上,
\[f_i=p_{i-1}-f_{i-1}-(i-1)\times(2^n-i+1)\times f_{i-2} \]線性遞推即可。
\(code:\)
T3
#include<bits/stdc++.h>
#define int long long
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=10000010,mod=1e9+7;
int n,ans,base,p[NN],f[NN];
namespace Combination{
int fac[NN],inv[NN];
int C(int x,int y){ return x<0||y<0||x<y?0:fac[x]*inv[y]%mod*inv[x-y]%mod; }
int qpow(int a,int b){
int res=1;
for(;b;b>>=1){
if(b&1) res=res*a%mod;
a=a*a%mod;
}
return res;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
inv[n]=qpow(fac[n],mod-2);
for(int i=n-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
} using namespace Combination;
signed main(){
freopen("yui.in","r",stdin);
freopen("yui.out","w",stdout);
n=read(); init(); base=qpow(2,n); p[0]=1;
for(int i=1;i<=n;i++) p[i]=p[i-1]*(base-i)%mod;
for(int i=3;i<=n;i++)
f[i]=(p[i-1]+mod-f[i-1]+mod-(i-1)*(base-i+1)%mod*f[i-2]%mod)%mod;
ans=(mod+p[n]-f[n])%mod;
write(ans,'\n');
return 0;
}
T4 七負我
既然真白你說不要的話,神田君就歸我了哦。
結論:最後答案為取圖中最大團,平均分配點權後得到的答案。
證明團:
設\(u,v\)為互不相連的兩點,設\(s_u,s_v\)分別為與\(u,v\)相連的點權和,那麼當\(s_u\neq s_v\)時,顯然\(u,v\)其中一點權值為\(0\)。\(s_u=s_v\)時也可保證\(u,v\)其中一點權值為\(0\)是一種最優解。因此兩個不連通的點間總有一點不會被分配權值,即最終答案會出現在團中。
證明平均分配:
設\(t_i\)為團中第\(i\)個點的權值,\(s\)為團大小,則\(x=\sum_{i=1}^st_i\),有
\[ans=\sum_{u\neq v} t_u\times t_v \]\[ans=\frac{x^2-\sum_{i=1}^st_i^2}{2} \]由均值不等式,當\(t_i\)都相等時\(ans\)取最大值。
證明最大:
設團大小為\(s\),則
\[ans=\frac{s\times(s-1)}{2}\times(\frac{x}{s})^2 \]\[ans=\frac{(s-1)\times x^2}{2s}=x^2\times(\frac{1}{2}-\frac{1}{2s}) \]\(s\)越大,\(\frac{1}{2s}\)越趨近於\(0\),答案取值越大。
綜上可得最上面的結論。
\(meet\;in\;the\;middle\),找到前一半所有點集的最大團子集,不妨設點集\(s\)的最大團子集為\(f_s\),之後列舉後一半點集\(t\),若\(t\)為團,令\(e\)為\(t\)中所有點的鄰接點在前一部分的交集,則\(|t|+f_e\)可以成為答案。
\(code:\)
T4
#include<bits/stdc++.h>
using namespace std;
namespace IO{
typedef long long LL;
int read(){
LL x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
void write(int x,char sp){
char ch[20]; int len=0;
if(x<0) x=-x,putchar('-');
do{ ch[len++]=x%10+'0'; x/=10; }while(x);
for(int i=len-1;~i;i--) putchar(ch[i]); putchar(sp);
}
void ckmin(int& x,int y){ x=x<y?x:y; }
void ckmax(int& x,int y){ x=x>y?x:y; }
} using namespace IO;
const int NN=45;
int n,m,x,lmt,ans,f[1<<20];
int ef[NN],eb[NN];
double res;
int getnum(int s){
int res=0;
while(s) res+=(s&1),s>>=1;
return res;
}
bool check(int s,bool typ){
for(int i=0;i<lmt;i++) if(s&(1<<i))
if(((s^(1<<i))|ef[i+1+typ*lmt])!=ef[i+1+typ*lmt]) return 0;
return 1;
}
signed main(){
freopen("nanami.in","r",stdin);
freopen("nanami.out","w",stdout);
n=read(); m=read(); x=read(); lmt=n+1>>1;
for(int u,v,i=1;i<=m;i++){
u=read(); v=read();
if(u>v) swap(u,v);
if(v<=lmt){
ef[u]|=1<<v-1;
ef[v]|=1<<u-1;
} else if(u>lmt){
ef[u]|=1<<v-1-lmt;
ef[v]|=1<<u-1-lmt;
} else eb[v]|=1<<u-1;
}
for(int i=1;i<(1<<lmt);i++)
if(check(i,0)) ckmax(ans,f[i]=getnum(i));
for(int s=1;s<(1<<lmt);s++)
for(int i=0;i<lmt;i++)
if(!(s&(1<<i))) ckmax(f[s|(1<<i)],f[s]);
for(int s=1;s<(1<<lmt);s++) if(check(s,1)){
int tmp=(1<<lmt)-1;
for(int i=0;i<lmt;i++)
if(s&(1<<i)) tmp&=eb[i+1+lmt];
ckmax(ans,getnum(s)+f[tmp]);
}
res=(1.0*x/ans)*(1.0*x/ans)*(ans-1)*ans/2;
printf("%.6lf\n",res);
return 0;
}