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
  • 相关阅读:
    java多线程学习-同步之线程通信
    java多线程学习-同步(synchronized)
    java多线程学习-开篇
    面向对象-多线程-异常机制-查漏补缺
    Sprin2.5+Hibernate3.3+Struts2.0.11集成
    Strut1.3+Sprin2.5+Hibernate3.3集成
    Sprin2.5+Hibernate3.3集成
    Spring学习笔记
    Hibernate学习笔记
    Sping
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12588033.html
Copyright © 2011-2022 走看看