zoukankan      html  css  js  c++  java
  • [考试反思]0328省选模拟56:漂浮

    考场上预估得分是$70+50+100=220$

    然后居然能炸成这个鬼德行。。。

    最近状态一直不好,今天难得大概是想到了两个正解,但是三题全挂细节。。。

    $T1$题目条件读丢了一个,少了$20pts$(读题出锅这咋治。。。)

    $T2$一眼看出是容斥但没时间细想少了一条特判把$50$的暴力也送了。

    $T3$没多少时间但是发现了规律然而变量名写错除了无解以外的$90$全部暴毙。

    好像是时间分配出问题了。完全没时间写对拍。

    最后一题连想带写只用了半小时,不出错才奇怪。

    大概还是应该至少稳住一道题吧。至少不会像今天这么惨。

    T1:取石子游戏

    大意:$n$个数,要求取出$k(k<n)$个,满足$k$是给定常数$d$的倍数,使得剩下的数异或i和为$0$。$n le 5 imes 10^5,a_i le 10^6,sum a_i le 10^7 d le 10$

    坑点是$k<n$,不能相等,要特判。

    最朴素的就是做一个类似模意义下的背包,复杂度$O(max(a)nd)$

    我最开始写的是从小到大加入,背包的值域不断扩大到当前最大物品的$2^{highbit(a)+1}$。然而写丑了变成$O(sum a_i d^2)$的了。

    然后如果按照从大到小加入物品的话,那么当$highbit(a_{i-1}) eq highbit(a_i)$时,我们就知道此时最高位不对的状态以后也再也达不到,直接舍弃。

    这样然后玄学的算一下复杂度,大约是$O(sum a_i d)$的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define mod 1000000007
     4 #define S 1048576
     5 int dp[10][S],t[10][S],n,c[S],d,v,b=1,fac[S],inv[S];
     6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
     7 int read(){
     8     register int p=0;register char ch=getchar();
     9     while(!isdigit(ch))ch=getchar();
    10     while(isdigit(ch))p=(p<<3)+(p<<1)+ch-48,ch=getchar();
    11     return p;
    12 }
    13 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
    14 void ctb(int n){
    15     while(b<=n)b<<=1;
    16     while(b>n)b>>=1;b<<=1;
    17     for(int i=0;i<d;++i)for(int j=0;j<b;++j)t[i==d-1?0:i+1][j^n]=dp[i][j];
    18     for(int i=0;i<d;++i)for(int j=0;j<b;++j)add(dp[i][j],t[i][j]);
    19 }
    20 int main(){//freopen("stone8.in","r",stdin);
    21     n=read(); d=read();
    22     for(int i=0;i<n;++i)c[read()]++;
    23     for(int i=fac[0]=1;i<S;++i)fac[i]=fac[i-1]*1ll*i%mod;
    24     inv[S-1]=qp(fac[S-1],mod-2);
    25     for(int i=S-2;~i;--i)inv[i]=inv[i+1]*(i+1ll)%mod;
    26     for(int i=0;i<S;++i)if(c[i]&1)v^=i;
    27     dp[0][v]=1;
    28     for(int i=S-1;i;--i)while(c[i])ctb(i),c[i]--;
    29     cout<<(dp[0][0]-(n%d==0))<<endl;
    30 }
    View Code

    T2:路径计数

    大意:有向图,多次询问$s,t,d$表示从$s$开始在$t$结束经过$d$条边且中途不经过$s,t$的方案数。$n le 100,m le n(n-1),d le 50,q le 10^6$

    暴力就是设$dp[i][j][k][now]$表示从$i$出发到$j$目前走了$k$步停在$now$上的方案数。$O(n^4d)$

    只有两个特殊点,看起来就像容斥。

    设$f[i][j][k]$表示$i$走到$j$用了$k$步且中途不经过$i,j$的方案数。再设$g[i][j][k]$表示从$i$到$j$可以经过任何点走了$k$步的方案数。

    $g$的转移比较简单。

    $f$的非法状态我们把它分类以不重不漏:我们按照每条非法路径经历的第一个非法点分类,那无非就是$i,j$两种。再枚举$d$表示到非法点为止走了几步。

    如果非法点是$j$,那么就是说用了$d$步从$i$走到$j$不经过$i,j$,然后从$j$任意走到$j$。这就分别是$f[i][j][d]$和$g[j][j][k-d]$

    如果非法点是$i$,那么就是说用了$d$步从$i$走到$i$不经过$i,j$,然后从$i$任意走到$j$。后者就是$g[i][j][k-d]$。前者不知道。

    于是我们再设一个$h[i][j][k]$表示不经过$i,j$从$i$走到$i$用了$k$步的方案数。

    这样我们就知道$f[i][j][k]=g[i][j][k] - sumlimits_{d=1}^{k-1} f[i][j][d] imes g[j][j][k-d]  +  sumlimits_{d=1}^{k-1} h[i][j][d] imes g[i][j][k-d]$

    要注意$i=j$时需要特判。非法点只有一个。

    考虑怎么求得$h[i][j][k]$。类似上面的思路,我们去分别考虑第一个非法点是什么。

    和上面一样讨论一下就能得到$h[i][j][k]=g[i][i][k] - sumlimits_{d=1}^{k-1} h[i][j][d] imes g[i][i][k-d]  +  sumlimits_{d=1}^{k-1} f[i][j][d] imes g[j][i][k-d]$

    同样要注意$i=j$的特判。这样,三个东西的状态数都是$O(n^2d)$的,$g$的转移数是$O(n)$的,$f,h$的转移数是$O(d)$的。

    所以总的复杂度是$O(n^3d+n^2d^2)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int read(){
     4     register int p=0;register char ch=getchar();
     5     while(!isdigit(ch))ch=getchar();
     6     while(isdigit(ch))p=(p<<3)+(p<<1)+ch-48,ch=getchar();
     7     return p;
     8 }
     9 int f[111][111][55],g[111][111][55],h[111][111][55],n,m,mod;
    10 //f:i->j (ban i j)  g:i->j  h:i->i(ban i j)
    11 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;if(a<0)a+=mod;}
    12 vector<int>v[111];
    13 int main(){
    14     n=read(),m=read(),mod=read();
    15     for(int i=0,a,b;i<m;++i)a=read(),b=read(),v[a].push_back(b);
    16     for(int i=1;i<=n;++i)f[i][i][0]=g[i][i][0]=h[i][i][0]=1;
    17     for(int d=1;d<55;++d){
    18         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int k:v[j])
    19             add(g[i][k][d],g[i][j][d-1]);
    20         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
    21             f[i][j][d]=g[i][j][d],h[i][j][d]=g[i][i][d];
    22         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int D=1;D<d;++D){
    23             f[i][j][d]=(f[i][j][d]-1ll*f[i][j][D]*g[j][j][d-D]%mod+mod)%mod;
    24             if(i!=j)f[i][j][d]=(f[i][j][d]-1ll*h[i][j][D]*g[i][j][d-D]%mod+mod)%mod;
    25         }
    26         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)for(int D=1;D<d;++D){
    27             h[i][j][d]=(h[i][j][d]-1ll*h[i][j][D]*g[i][i][d-D]%mod+mod)%mod;
    28             if(i!=j)h[i][j][d]=(h[i][j][d]-1ll*f[i][j][D]*g[j][i][d-D]%mod+mod)%mod;
    29         }
    30         //for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)printf("f[%d][%d][%d]=%d
    g[%d][%d][%d]=%d
    h[%d][%d][%d]=%d
    ",i,j,d,f[i][j][d],i,j,d,g[i][j][d],i,j,d,h[i][j][d]);
    31     }for(int q=read(),s,t,d;q--;)s=read(),t=read(),d=read(),printf("%d
    ",f[s][t][d]);
    32 }
    View Code

    T3:方格操作

    原题面:

    抽象题意:矩阵,初始给出$q$矩形,被包含次数为奇数的点为白色其余为黑色。每一轮开始前,代价增加(总白点数量)。

    然后把所有点弄成黑色后,对于每行每列若上一轮中有奇数个白点就翻转整行列的状态。不断执行每一轮直到所有点全变为黑色。求总代价(无穷则$-1$)

    $n,m,q le 10^5$

    把题意抽象成上面我说的这个模样,问题就很简单了。

    (我把白点所在位置要翻转$1$次,变成了自己翻转一次,行一次,列一次一共$3$次,显然对答案没影响,但是有了自己翻转一次就有抽象题意中的“所有点弄成黑色”就好做些了)

    首先第一轮的代价可以直接线段树+扫描线解决。

    然后我们给每一行列都打上标记表示下一轮是否会被翻转。

    可以发现矩阵两行相交换没有影响,列同理。于是只需要记录有多少行列会被翻转即可。

    然后就可以模拟了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 111111
     4 int v[S<<2],lz[S<<2],n,m,q,row[S],col[S],c1,d1;long long ans;
     5 #define lc p<<1
     6 #define rc lc|1
     7 #define md (L+R>>1)
     8 vector<int>ll[S],rr[S]; map<int,int>M[S];
     9 void modify(int l,int r,int p=1,int L=1,int R=m){
    10     if(l<=L&&R<=r){v[p]=R-L+1-v[p];lz[p]^=1;return;}
    11     if(lz[p])v[lc]=md-L+1-v[lc],v[rc]=R-md-v[rc],lz[lc]^=1,lz[rc]^=1,lz[p]=0;
    12     if(l<=md)modify(l,r,lc,L,md); if(r>md)modify(l,r,rc,md+1,R); v[p]=v[lc]+v[rc];
    13 }
    14 int main(){
    15     cin>>n>>m>>q;
    16     for(int i=1,x,y,X,Y;i<=q;++i){
    17         scanf("%d%d%d%d",&x,&y,&X,&Y);
    18         X++;Y++;
    19         row[x]^=(Y^y)&1;row[X]^=(Y^y)&1;
    20         col[y]^=(X^x)&1;col[Y]^=(X^x)&1;
    21         ll[x].push_back(y);ll[X].push_back(y);
    22         rr[x].push_back(Y-1);rr[X].push_back(Y-1);
    23     }
    24     for(int i=1;i<=n;++i){
    25         for(int j=0;j<ll[i].size();++j)modify(ll[i][j],rr[i][j]);
    26         ans+=v[1];cerr<<ans<<endl;
    27     }
    28     for(int i=1;i<=n;++i)row[i]^=row[i-1],c1+=row[i];
    29     for(int i=1;i<=m;++i)col[i]^=col[i-1],d1+=col[i];
    30     while(1){
    31         if(M[c1][d1])return puts("-1"),0; M[c1][d1]=1;
    32         if(1ll*c1*(m-d1)+1ll*d1*(n-c1)==0)return cout<<ans,0;
    33         ans+=1ll*c1*(m-d1)+1ll*d1*(n-c1);
    34         int C1=c1,D1=d1;
    35         c1=C1*((m-D1)&1)+(n-C1)*(D1&1);
    36         d1=D1*((n-C1)&1)+(m-D1)*(C1&1);
    37     }
    38 }
    View Code
  • 相关阅读:
    POJ 1300 Open Door
    POJ 2230 Watchcow
    codevs 1028 花店橱窗布置
    codevs 1021 玛丽卡
    codevs 1519 过路费
    codevs 3287 货车运输
    codevs 3305 水果姐逛水果街二
    codevs 1036 商务旅行
    codevs 4605 LCA
    POJ 1330 Nearest Common Ancestors
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12588033.html
Copyright © 2011-2022 走看看