noip模拟51
A. 茅山道术
比较智障的一道\(dp\),然而我没想到.
老感觉像个数学题..
A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define re register ll
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll N=1e6+21,mod=1e9+7;
ll n,m,ans;
ll dp[N],val[N],pre[N],tot[N];
signed main(){
File(magic.in,magic.out);
n=read(); ll tmp=0,cnt=0;
for(re i=1;i<=n;i++) if((tmp=read())!=val[cnt]) val[++cnt]=tmp;
n=cnt;
for(re i=1;i<=n;i++){
dp[i]=(pre[val[i]]+tot[val[i]]+dp[i-1])%mod;
tot[val[i]]++,pre[val[i]]=(pre[val[i]]+dp[i-1])%mod;
}
printf("%lld\n",(dp[n]+1)%mod);
exit(0);
}
B. 泰拳警告
比较智障的一道数学题,乱画画就行.
B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define re register ll
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const ll mod=998244353,N=3e6+21;
ll m,n,wl,pj,ans,pow2;
ll frac[N],poj[N],pol[N],pot[N],inv[N],dn[N],c[N];
inline ll ksm(ll a,ll b,ll c){
a%=c; ll res=1;
while(b){
if(b&1) res=(res*a)%mod;
a=(a*a)%mod,b>>=1;
}
return res%mod;
}
inline ll C(ll a,ll b){
if(a>b) return 0;
return frac[b]*ksm(frac[a]*frac[b-a],mod-2,mod)%mod;
}
signed main(){
freopen("fight.in","r",stdin),freopen("fight.out","w",stdout);
n=read(),m=read(),wl=ksm(m+2,mod-2,mod),pj=wl*m%mod,
pow2=ksm(2,mod-2,mod),
frac[0]=1,poj[0]=1,pol[0]=1,pot[0]=1,dn[0]=1,c[0]=1,inv[1]=1;
for(ll i=1;i<=3000000;i++){
frac[i]=frac[i-1]*i%mod,pot[i]=(pot[i-1]<<1)%mod,
poj[i]=poj[i-1]*pj%mod,pol[i]=pol[i-1]*wl%mod;
if(i>1) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
for(re i=0;i<=n;i++){
dn[i+2]=dn[i]*(i+1)%mod*(i+2)%mod,
dn[i+2]=dn[i+2]*inv[(i>>1)+1]%mod*inv[(i>>1)+1]%mod,
c[i+1]=c[i]*(n-i)%mod*inv[i+1]%mod;
}
ll res,tmp;
for(ll i=0;i<n;i++){
tmp=n-i;
res=pot[tmp]-dn[tmp]*(!(tmp&1))+mod,res=res%mod*pow2%mod,
res=res*pol[tmp]%mod*poj[i]%mod*c[i]%mod;
ans=(ans+res*(i+1)%mod)%mod;
}
printf("%lld\n",ans);
exit(0);
}
C. 万猪拱塔
发现很难直接做,考虑如何转化问题.
发现一个显然但不明显的性质:
某个矩形如果合法,那么这个矩形的权值一定是连续的.
发现一个很不明显的性质,
我们把整个矩形有重叠地划分为\((n+1)*(m+1)\)个小的\(2*2\)正方块.
如果我们把这个矩形进行染色,那么就只有四个小正方块的内部被染色了一个格子;同样的,如果对 \(l\) ~ \(r\) 染色,只有四个小正方块的内部被染了一个格子,且没有内部被染了三个格子的正方块,就说明这是一个矩形.即为充要条件.
定义一个 \(f_{l,r}\) 数组为,如果把权值位于 \(l\) ~ \(r\) 全都染色,有多少个小正方块内部含有奇数个被染色的格子.
考虑在改变 \(r\) 时更新 \(l\) 的值,发现只会对权值为 \(r\) 的点所被包含四个小正方形有影响.
这边建议自己举例乱糊一下,不再赘述了..
于是线段树维护即可.
C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define re register ll
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
#define ls x<<1
#define rs x<<1|1
const ll mod=998244353,N=2e5+21;
ll m,n,ans;
ll c[25];
unordered_map<ll,ll> val[N];
struct I { ll cnt,lzy,sumw,minw; } tr[N<<2],ret;
struct II { ll x,y; } p[N];
inline void getval(ll x,ll w){
tr[x].minw+=w,tr[x].lzy+=w;
}
inline void spread(ll x,ll l,ll r){
ll &lzy=tr[x].lzy,mid=(l+r)>>1;
if(lzy) getval(ls,lzy),getval(rs,lzy),lzy=0;
}
inline void pushup(ll x){
ll &minw=tr[x].minw;
minw=min(tr[ls].minw,tr[rs].minw);
tr[x].cnt=tr[ls].cnt*(minw==tr[ls].minw)+tr[rs].cnt*(minw==tr[rs].minw),
tr[x].sumw=tr[ls].sumw*(minw==tr[ls].minw)+tr[rs].sumw*(minw==tr[rs].minw);
}
void update(ll x,ll l,ll r,ll ql,ll qr,ll w){
if(ql>qr) return;
if(l>=ql and r<=qr) return getval(x,w),void();
ll mid=(l+r)>>1; spread(x,l,r);
if(ql<=mid) update(ls,l,mid,ql,qr,w);
if(qr>mid) update(rs,mid+1,r,ql,qr,w);
pushup(x);
}
void pushdown(ll x,ll l,ll r,ll ql,ll qr){
if(l>=ql and r<=qr){
if(tr[x].minw==4) ret.sumw+=tr[x].sumw,ret.cnt+=tr[x].cnt;
return ;
}
ll mid=(l+r)>>1; spread(x,l,r);
if(ql<=mid) pushdown(ls,l,mid,ql,qr);
if(qr>mid) pushdown(rs,mid+1,r,ql,qr);
pushup(x);
}
void build(ll x,ll l,ll r){
if(l==r) return tr[x].cnt=1,tr[x].sumw=l,void();
ll mid=(l+r)>>1;
build(ls,l,mid),build(rs,mid+1,r);
pushup(x);
}
inline void solve(ll x,ll y,ll w){
ll cnt=0,p,k=1;
c[++cnt]=val[x][y],c[++cnt]=val[x-1][y-1],
c[++cnt]=val[x-1][y],c[++cnt]=val[x][y-1];
sort(c+1,c+1+cnt),p=lb(c+1,c+1+cnt,w)-c;
for(p;p>=1;p--,k*=-1){
update(1,1,n,c[p-1]+1,c[p],k);
}
}
signed main(){
File(pig.in,pig.out);
n=read(),m=read(); ll w,x,y,cnt;
for(int i=0;i<=n+1;i++)
val[0][i]=N,val[n+1][i]=N,val[i][0]=N,val[i][n+1]=N;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
val[i][j]=w=read(),p[w].x=i,p[w].y=j;
}
n*=m,build(1,1,n);
for(re i=1;i<=n;i++){
solve(p[i].x,p[i].y,i),solve(p[i].x+1,p[i].y+1,i),
solve(p[i].x+1,p[i].y,i),solve(p[i].x,p[i].y+1,i);
pushdown(1,1,n,1,i);
// cout<<ret.cnt<<' '<<ret.sumw<<endl;
// cout<<"res:"<<(i+1)*ret.cnt-ret.sumw<<endl;
// cout<<tr[1].minw<<endl;
ans=(ans+(i+1)*ret.cnt-ret.sumw+mod)%mod;
ret.cnt=0,ret.sumw=0;
}
printf("%lld\n",ans);
exit(0);
}
D. 抑郁刀法
考虑推一推性质.
发现对于一些出入度等于 \(1\) 的边来说.
我们选择删掉 \(ta\) 们之后再统计答案,最后再乘上 \(k-1\) 就好了.
到这里其实是应该再想一想就能 \(get\) 到正解的.
发现数据范围给出的 \(m\) 和 \(n\) 很接近.
所以是不是删掉 \(d=2\) 的点也行..?
发现的确就是这样,然而我没想到,考场上也确实没有这么多时间思考.
沿着上面的思路,考虑删掉 \(d=2\) 的点,
但是要再在两个点之间连一条新边,并赋个权.
分别记录两个点为 \(a,b\).
设 \(f_{a,b}\) 为 \(a,b\) 同色时答案乘的系数,\(g_{a,b}\) 为 \(a,b\) 异色时答案乘的系数.
但是当要删的两条边也有权,那就要算一算了.
设要删的两条边原来的 \(f\) 和 \(g\) ,分别为 \(f_1,g_1,f_2,g_2\).
那么新的 \(f=(k-1)*g_1*g_2+f_1*f_2\),\(g=(k-2)*g_1*g_2+f_1*g_2+f_2*g_1\).
删完之后暴力做状压就可以了.
另外,状压的时候枚举的范围注意不要写成 \(n\) 或 \(k\),合并边的时候注意要先把之前的 \(f\) 和 \(g\) 乘起来再把之前的边删掉.
关于如何想到..
其实看到 \(n\) 给出了 \(\le 10\) 的数据之后发现 \(m\le n+5\),
就可以逆向考虑这个题目了,
大概就是暴力不能直接做,那就选择把题目数据变成暴力的思想.
D_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS
{
#define ll long long int
#define lf double
#define ull unsigned ll
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define Fill(x,y) memset(x,y,sizeof x)
#define Copy(x,y) memcpy(x,y,sizeof x)
#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
#define FILE(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
inline ll read()
{
ll ss=0; bool cit=1; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') cit=0;
while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
return cit?ss:-ss;
}
} using namespace BSS;
const int N=1e5+21,mod=1e9+7;
ll m,n,r,ans,sum,ts=1,cnt;
ll frc[N],ban[N],head[N],vis[N],d[N],rk[N],pot[N];
ll dp[1<<17][17];
struct I { ll u,v,f,g,nxt,lst; } e[N<<3];
queue<ll> que;
map<ll,ll> mp1[N];
inline ll ksm(ll a,ll b,ll c){
a%=c; ll res=1;
while(b){
if(b&1) res=(res*a)%c;
a=(a*a)%c,b>>=1;
}
return res%c;
}
inline ll C(ll a,ll b){
if(a>b) return 0;
return frc[b]*ksm(frc[a]*frc[b-a],mod-2,mod)%mod;
}
inline void del(ll i){
ll x,y;
x=e[i].lst,y=e[i].nxt;
e[x].nxt=y,e[y].lst=x;
if(i==head[e[i].u]) head[e[i].u]=y;
}
inline void add(ll u,ll v,ll f,ll g){
e[++ts].u=u,e[ts].v=v,e[ts].nxt=head[u],e[head[u]].lst=ts;
head[u]=ts;
if(mp1[u][v]==0)e[ts].f=f,e[ts].g=g,mp1[u][v]=ts;
else e[ts].f=f*e[mp1[u][v]].f%mod,e[ts].g=g*e[mp1[u][v]].g%mod,del(mp1[u][v]);
// cout<<u<<' '<<v<<' '<<e[ts].f<<" "<<e[ts].g<<endl;
mp1[u][v]=ts;
}
inline void bfs(){
for(int i=1;i<=n;i++){
if(d[i]==1) que.push(i),sum=sum*(r-1)%mod,vis[i]=1;
}
ll u,v,v1,v2,f1,f2,g1,g2,f,g;
while(que.size()){
u=que.front(),que.pop();
for(int i=head[u];i;i=e[i].nxt){
if(vis[e[i].v]) continue;
if((--d[e[i].v])==1){
que.push(e[i].v),sum=sum*(r-1)%mod,vis[e[i].v]=1;
}
else del(i),del(i^1);
}
}
for(int u=1;u<=n;u++){
if(d[u]==2){
v1=e[head[u]].v,f1=e[head[u]].f,g1=e[head[u]].g,del(head[u]^1),del(head[u]);
v2=e[head[u]].v,f2=e[head[u]].f,g2=e[head[u]].g,del(head[u]^1),del(head[u]);
if((!v1) or (!v2)) continue;
f=(r-1)*g1%mod*g2%mod+f1*f2%mod,g=(r-2)*g1%mod*g2%mod+f1*g2%mod+f2*g1%mod,f%=mod,g%=mod;
add(v1,v2,f,g),add(v2,v1,f,g);
vis[u]=1;
}
}
}
signed main(){
n=read(),m=read(),r=read(),frc[0]=1,sum=1; ll u,v,U,S,res;
for(ll i=1;i<=1e5;i++) frc[i]=frc[i-1]*i%mod;
for(int i=1;i<=m;i++){
u=read(),v=read(),d[u]++,d[v]++;
add(u,v,0,1),add(v,u,0,1);
}
bfs();
for(int i=1;i<=n;i++){
if(!vis[i]) pot[++cnt]=i,rk[i]=cnt;
}
dp[0][0]=1; U=(1<<cnt)-1;
for(int i=0;i<=U;i++){
S=U xor i;
for(int s=S;s;(--s)&=S){
res=1;
for(int j=1;j<=cnt;j++){
if(!((s>>j-1)&1)) continue;
for(int k=j;k<=cnt;k++){
if(!((s>>k-1)&1)) continue;
if(mp1[pot[j]].find(pot[k])==mp1[pot[j]].end()) continue;
res=(res*e[mp1[pot[j]][pot[k]]].f)%mod;
}
for(int k=1;k<=cnt;k++){
if(!((i>>k-1)&1)) continue;
if(mp1[pot[j]].find(pot[k])==mp1[pot[j]].end()) continue;
res=(res*e[mp1[pot[j]][pot[k]]].g)%mod;
}
}
for(int j=0;j<cnt;j++){
dp[i|s][j+1]=(dp[i|s][j+1]+dp[i][j]*res%mod)%mod;
}
}
}
for(int i=1;i<=r;i++) ans=(ans+dp[U][i]*C(i,r)%mod)%mod;
if(!cnt) ans=r*ksm(r-1,mod-2,mod)%mod; ans=ans*sum%mod;
printf("%lld\n",ans),exit(0);
}

浙公网安备 33010602011771号