noip模拟51[好IOI]
noip模拟51 solutions
好像是和别的学校一起考得
然后我交错比赛了,变成了\(IOI\)赛制,交上去就是俩\(AC\)
所以这次的题前两个过于水了。。。。
T1 茅山道术
就是找在当前条件下的,序列不重合划分的方案数,直接\(DP\),类似前缀和优化一下
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e6+5;
const int mod=1e9+7;
int n,c[N];
vector<int> vec[N];
int dp[N],sum[N],pos[N];
signed main(){
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
scanf("%d",&n);
for(re i=1;i<=n;i++)scanf("%d",&c[i]);
n=unique(c+1,c+n+1)-c-1;
for(re i=1;i<=n;i++)vec[c[i]].push_back(i),pos[i]=vec[c[i]].size()-1;
dp[0]=0;
for(re i=1;i<=n;i++){
dp[i]=dp[i-1];
if(pos[i])dp[i]=(dp[i]+sum[vec[c[i]][pos[i]-1]-1]+pos[i])%mod;
if(pos[i])sum[i-1]=(dp[i-1]+sum[vec[c[i]][pos[i]-1]-1])%mod;
else sum[i-1]=dp[i-1];
//cout<<dp[i]<<" "<<sum[i-1]<<" "<<sum[vec[c[i]][pos[i]-1]-1]<<" "<<pos[i]<<endl;
}
//cout<<endl;
printf("%d",dp[n]+1);
}
T2 泰拳警告
直接找规律,然后统计答案就好了
枚举平了多少局,后面第一个人必须胜一半以上
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register int
const int N=3e6+5;
const int mod=998244353;
int n,p,pf[N],pg[N],jc[N],inv[N],ans,n2,m2[N];
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;
y>>=1;
}
return ret;
}
int C(int x,int y){
return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
freopen("fight.in","r",stdin);
freopen("fight.out","w",stdout);
scanf("%lld%lld",&n,&p);
pf[0]=pg[0]=1;n2=ksm(2,mod-2);
pf[1]=ksm(p+2,mod-2);pg[1]=p*ksm(p+2,mod-2)%mod;
jc[0]=jc[1]=1;m2[0]=1;m2[1]=2;
for(re i=2;i<=n;i++){
pf[i]=pf[i-1]*pf[1]%mod;
pg[i]=pg[i-1]*pg[1]%mod;
jc[i]=jc[i-1]*i%mod;
m2[i]=m2[i-1]*2%mod;
}
inv[0]=1;inv[n]=ksm(jc[n],mod-2);
for(re i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
//cout<<ksm(9,mod-2)<<" "<<ksm(3,mod-2)<<endl;
for(re i=0;i<=n;i++){
int tmp;
if((n-i)&1)tmp=m2[n-i]*n2%mod*C(n,i)%mod;
else tmp=(m2[n-i]*n2%mod+mod-C(n-i,n-i>>1)*n2%mod)%mod*C(n,i)%mod;
ans=(ans+pg[i]*pf[n-i]%mod*(i+1)%mod*tmp%mod)%mod;
//cout<<pf[i]<<" "<<pg[n-i]<<" "<<ans<<endl;
}
printf("%lld",ans);
}
T3 万猪拱塔
首先看题要看准,\(k\)是互不相同的
所以我当前矩形的数一定是一个连续的区间
直接枚举这个区间判断是否构成矩形
按着题解上说的,枚举右端点,更新所在的四个小矩形,线段树
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register int
const int N=2e5+5;
const int mod=998244353;
int n,m,ans,rp;
vector<int> jz[N],id[N];
struct position{
int i,j;
position(){}
position(int x,int y){i=x;j=y;}
}ti[N];
struct XDS{
#define ls x<<1
#define rs x<<1|1
int sum[N*4],csm[N*4],res[N*4],tag[N*4];
int re_csm,re_res;
void pushup(int x){
sum[x]=min(sum[ls],sum[rs]);csm[x]=0;res[x]=0;
if(sum[x]==sum[ls])csm[x]+=csm[ls],res[x]+=res[ls];
if(sum[x]==sum[rs])csm[x]+=csm[rs],res[x]+=res[rs];
}
void pushdown(int x){
if(!tag[x])return ;
tag[ls]+=tag[x];
tag[rs]+=tag[x];
sum[ls]+=tag[x];sum[rs]+=tag[x];
tag[x]=0;
}
void build(int x,int l,int r){
if(l==r){
csm[x]=1;sum[x]=0;res[x]=l;
return ;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);
return ;
}
void ins(int x,int l,int r,int ql,int qr,int v){
if(ql>qr)return ;
if(ql<=l&&r<=qr){
sum[x]+=v;tag[x]+=v;
return ;
}
pushdown(x);
int mid=l+r>>1;
if(ql<=mid)ins(ls,l,mid,ql,qr,v);
if(qr>mid)ins(rs,mid+1,r,ql,qr,v);
pushup(x);return ;
}
void query(int x,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){
if(sum[x]==4)re_csm+=csm[x],re_res+=res[x];
return ;
}
pushdown(x);
int mid=l+r>>1;
if(ql<=mid)query(ls,l,mid,ql,qr);
if(qr>mid)query(rs,mid+1,r,ql,qr);
pushup(x);
}
#undef ls
#undef rs
}xds;
int ji[5],cnt;
signed main(){
freopen("pig.in","r",stdin);
freopen("pig.out","w",stdout);
scanf("%lld%lld",&n,&m);
jz[0].resize(m+5);jz[n+1].resize(m+5);
for(re i=1;i<=n;i++){
jz[i].resize(m+5);
id[i].resize(m+5);
for(re j=1;j<=m;j++){
scanf("%lld",&jz[i][j]);
ti[jz[i][j]]=position(i,j);
}
}
//cout<<"Sb"<<endl;
xds.build(1,1,n*m);
for(re i=1;i<=n*m;i++){
int x=ti[i].i,y=ti[i].j;
cnt=0;
if(jz[x-1][y-1]<i)ji[++cnt]=jz[x-1][y-1];
if(jz[x][y-1]<i)ji[++cnt]=jz[x][y-1];
if(jz[x-1][y]<i)ji[++cnt]=jz[x-1][y];
sort(ji+1,ji+cnt+1);
xds.ins(1,1,n*m,ji[cnt]+1,i,1);
for(re j=cnt,bas=-1;j>=1;j--)xds.ins(1,1,n*m,ji[j-1]+1,ji[j],bas),bas=-bas;
cnt=0;
if(jz[x-1][y+1]<i)ji[++cnt]=jz[x-1][y+1];
if(jz[x][y+1]<i)ji[++cnt]=jz[x][y+1];
if(jz[x-1][y]<i)ji[++cnt]=jz[x-1][y];
sort(ji+1,ji+cnt+1);
xds.ins(1,1,n*m,ji[cnt]+1,i,1);
for(re j=cnt,bas=-1;j>=1;j--)xds.ins(1,1,n*m,ji[j-1]+1,ji[j],bas),bas=-bas;
cnt=0;
if(jz[x+1][y-1]<i)ji[++cnt]=jz[x+1][y-1];
if(jz[x][y-1]<i)ji[++cnt]=jz[x][y-1];
if(jz[x+1][y]<i)ji[++cnt]=jz[x+1][y];
sort(ji+1,ji+cnt+1);
xds.ins(1,1,n*m,ji[cnt]+1,i,1);
for(re j=cnt,bas=-1;j>=1;j--)xds.ins(1,1,n*m,ji[j-1]+1,ji[j],bas),bas=-bas;
cnt=0;
if(jz[x+1][y+1]<i)ji[++cnt]=jz[x+1][y+1];
if(jz[x][y+1]<i)ji[++cnt]=jz[x][y+1];
if(jz[x+1][y]<i)ji[++cnt]=jz[x+1][y];
sort(ji+1,ji+cnt+1);
xds.ins(1,1,n*m,ji[cnt]+1,i,1);
for(re j=cnt,bas=-1;j>=1;j--)xds.ins(1,1,n*m,ji[j-1]+1,ji[j],bas),bas=-bas;
xds.re_csm=xds.re_res=0;
xds.query(1,1,n*m,1,i);
//cout<<xds.re_res<<" "<<xds.re_csm<<endl;
(ans+=1ll*i*xds.re_csm-xds.re_res+xds.re_csm)%=mod;
}
printf("%lld",ans);
}
T4 抑郁刀法
这个首先要找到20pts的做法,直接状压dp,\(dp[i][j]\)表示\(i\)状态下用了\(j\)个颜色
注意要乘上一个组合数的
然后你发现这个图好像边特别少啊
直接先把1度点干掉,再把2度点干掉
再dp
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int mod=1e9+7;
int n,m,k,ans=1;
struct EDGE{int to,id;};
vector<EDGE> edg[N];
int du[N],f[N<<1],g[N<<1],p[N];
bool vpot[N],vedg[N<<1],vpof[N],vis[N<<1];
int id[N],rd[N],nn,jc[N],inv[N];
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int C(int x,int y){if(y>x)return 0;return jc[x]*inv[y]%mod*inv[x-y]%mod;}
void del1(){
queue<int> q;
for(int i=1;i<=n;i++)if(du[i]==1)q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
vpot[x]=true;ans=ans*(k-1)%mod;
for(auto i:edg[x]){
int y=i.to;
if(vpot[y])continue;
du[y]--;vedg[i.id]=true;
if(du[y]==1)q.push(y);
}
}
memset(du,0,sizeof(du));
for(int i=1;i<=n;i++){
vector<EDGE> tmp=edg[i];edg[i].clear();
for(auto j:tmp)if(!vpot[j.to])edg[i].push_back(j),du[i]++;
}
}
void del2(){
for(int i=1;i<=n;i++){
if(du[i]!=2 or vpot[i] or vpof[i])continue;
int x=edg[i][0].to,ix=edg[i][0].id;
int y=edg[i][1].to,iy=edg[i][1].id;
vedg[ix]=vedg[iy]=true;vpot[i]=true;
int nid=++m;
f[nid]=(f[ix]*g[iy]%mod+g[ix]*f[iy]%mod+(k-2)*f[ix]%mod*f[iy]%mod)%mod;
g[nid]=(g[ix]*g[iy]%mod+(k-1)*f[ix]%mod*f[iy]%mod)%mod;
if(x==y)vpof[x]=true,f[nid]=0;
for(auto &j:edg[x])if(j.to==i)j=EDGE{y,nid};
for(auto &j:edg[y])if(j.to==i)j=EDGE{x,nid};
}
for(int i=1;i<=n;i++)if(!vpot[i])++nn,id[nn]=i,rd[i]=nn;n=nn;
for(int i=1;i<=n;i++){
vector<EDGE> tmp=edg[id[i]];edg[id[i]].clear();
for(auto j:tmp){
if(j.to==id[i]){if(!vis[j.id])ans=ans*g[j.id]%mod,vis[j.id]=true;continue;}
if(!vedg[j.id])edg[id[i]].push_back(j);
}
}
}
int dp[1<<12][12],qa[1<<12];
int get(int s,int t){
int ret=1;
for(int i=1;i<=n;i++){
if(!(s&(1<<i-1)))continue;
for(auto j:edg[id[i]])
if(t&(1<<rd[j.to]-1))
ret=ret*f[j.id]%mod;
}
return ret;
}
void sol(){
dp[0][0]=1;int S=(1<<n)-1,res=0;
for(int i=1;i<=S;i++){qa[i]=1;
for(int j=1;j<=n;j++){
if(!(i&(1<<j-1)))continue;
for(auto k:edg[id[j]]){
if(rd[k.to]<j||!(i&(1<<rd[k.to]-1)))continue;
qa[i]=qa[i]*g[k.id]%mod;//cout<<i<<" "<<k.id<<" "<<g[k.id]<<endl;
}
}
}
for(int i=0;i<=S;i++){
for(int j=0;j<n;j++){
//if(!dp[i][j])continue;
int u=S^i;
for(int k=u;k;k=(k-1)&u)
dp[i|k][j+1]=(dp[i|k][j+1]+dp[i][j]*get(i,k)%mod*qa[k])%mod;//cout<<i<<" "<<k<<" "<<j<<" "<<n<<" "<<get(i,k)<<" "<<qa[k]<<endl;
}
}
//cout<<n<<" "<<qa[1]<<endl;
for(int i=1;i<=n;i++)res=(res+dp[S][i]*C(k,i))%mod;//cout<<S<<" "<<i<<" "<<dp[S][i]<<endl;
//cout<<res<<endl;
ans=ans*res%mod;
}
signed main(){
freopen("knife.in","r",stdin);
freopen("knife.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1,x,y;i<=m;i++){
scanf("%lld%lld",&x,&y);
du[x]++;du[y]++;f[i]=1;
edg[x].push_back(EDGE{y,i});
edg[y].push_back(EDGE{x,i});
}
jc[0]=1;for(int i=1;i<=k;i++)jc[i]=jc[i-1]*i%mod;
inv[0]=1;inv[k]=ksm(jc[k],mod-2);
for(int i=k-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
del1();//cout<<"del1"<<" "<<ans<<endl;
del2();//cout<<"del2"<<" "<<ans<<endl;
sol();
printf("%lld",ans);
}
QQ:2953174821