2019.3.16 noiac的原题模拟赛

RT,这太野蛮了,我不承认这是模拟赛

但是虽然是搬了三道题,题目本身也还能看,就这么着吧

(怎么机房里就我一道原题都没做过啊

T1 CF24D Broken Robot

比较简单地列出式子之后,我们发现可以自底向上每行做高斯消元求从每个格子出发的期望步数,复杂度$O(n^4)$(边界是最底下一行都是零)

然后我们发现高斯消元的时候每一行对应的方程就那几个地方有数,于是手动高斯消元一下就可以$O(n^2)$了

 1 #pragma GCC optimize(2)
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=1005,mod=998244353;
 7 int n,m,x,y,inv2,inv3,inv4,in2v,in3v,in4v,in23v,in34v;
 8 int dp[N][N],equ[N][N];
 9 template<class T,class TT> void Mis(TT &x,T y){x-=y; if(x<0) x+=mod;}
10 template<class T,class TT> int Mi(TT x,T y){x-=y; if(x<0) x+=mod; return x;}
11 void exGCD(int a,int b,int &x,int &y)
12 {
13     if(!b) x=1,y=0;
14     else exGCD(b,a%b,y,x),y-=a/b*x;
15 }
16 int Inv(int x)
17 {
18     int xx,yy; 
19     exGCD(x,mod,xx,yy); 
20     return (xx%mod+mod)%mod;
21 }
22 void Pre()
23 {
24     inv2=Inv(2),inv3=Inv(3),inv4=Inv(4);
25     in2v=mod-inv2,in3v=mod-inv3,in4v=mod-inv4;
26     in23v=2ll*in3v%mod,in34v=3ll*in4v%mod;
27 }
28 void Init(int a)
29 {
30     if(m==1) 
31     {
32         equ[1][1]=in2v;
33         equ[1][m+1]=Mi(1ll*dp[a+1][1]*in2v%mod,1);
34     }
35     else
36     {
37         equ[1][1]=equ[m][m]=in23v;
38         equ[1][2]=equ[m][m-1]=inv3;
39         equ[1][m+1]=Mi(1ll*dp[a+1][1]*in3v%mod,1);
40         equ[m][m+1]=Mi(1ll*dp[a+1][m]*in3v%mod,1);
41         for(int i=2;i<m;i++)
42         {
43             equ[i][i-1]=equ[i][i+1]=inv4,equ[i][i]=in34v;
44             equ[i][m+1]=Mi(1ll*dp[a+1][i]*in4v%mod,1);
45         }
46     }
47 }
48 void Guass(int a)
49 {
50     register int i;
51     for(i=1;i<m;i++)
52     {
53         int calc=1ll*equ[i+1][i]*Inv(equ[i][i])%mod;
54         Mis(equ[i+1][i],1ll*calc*equ[i][i]%mod);
55         Mis(equ[i+1][i+1],1ll*calc*equ[i][i+1]%mod);
56         Mis(equ[i+1][m+1],1ll*calc*equ[i][m+1]%mod);
57     }
58     dp[a][m]=1ll*equ[m][m+1]*Inv(equ[m][m])%mod;
59     for(i=m-1;i;i--)
60         dp[a][i]=1ll*Mi(equ[i][m+1],1ll*dp[a][i+1]*equ[i][i+1]%mod)*Inv(equ[i][i])%mod;
61 }
62 int main()
63 {
64     register int i;
65     scanf("%d%d%d%d",&n,&m,&x,&y),Pre();
66     for(i=n-1;i>=x;i--) Init(i),Guass(i);
67     printf("%d",dp[x][y]);
68     return 0;
69 }
View Code

T2 CEOI 2017 Building Bridges

花式优化DP

沙茶博主->CDQ+sort->$O(nlog^2 n)$

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define lli long long
 5 using namespace std;
 6 const int N=100005;
 7 int n,top,pos[N],stk[N];
 8 lli h[N],s[N],x[N],y[N],dp[N];
 9 void Maxi(lli &x,lli y){if(x<y) x=y;}
10 void Mini(lli &x,lli y){if(x>y) x=y;}
11 bool cmp(int a,int b)
12 {
13     return x[a]==x[b]?y[a]<y[b]:x[a]<x[b];
14 }
15 bool Slope(int a,int b,int c)
16 {
17     return (y[a]-y[c])*(x[b]-x[c])>=(y[b]-y[c])*(x[a]-x[c]);
18 }
19 lli Calc(int a,int b)
20 {
21     return -2*h[b]*x[a]+y[a];
22 }
23 void CDQ(int l,int r)
24 {
25     if(l==r)
26         x[l]=h[l],y[l]=dp[l]-s[l]+h[l]*h[l]; 
27     else
28     {
29         int mid=(l+r)>>1;
30         CDQ(l,mid);
31         sort(pos+l,pos+1+mid,cmp),top=0;
32         for(int i=l;i<=mid;i++)
33         {
34             while(top>1&&Slope(pos[i],stk[top-1],stk[top])) top--;
35             stk[++top]=pos[i];
36         }
37         for(int i=mid+1;i<=r;i++)
38         {
39             int ll=1,rr=top-1,re=top;
40             while(ll<=rr)
41             {
42                 int midd=(ll+rr)>>1;
43                 if(Calc(stk[midd],i)<Calc(stk[midd+1],i)) re=midd,rr=midd-1;
44                 else ll=midd+1;
45             }
46             Mini(dp[i],Calc(stk[re],i)+s[i-1]+h[i]*h[i]);
47         }
48         CDQ(mid+1,r);
49     }
50 }
51 int main()
52 {
53     scanf("%d",&n);
54     for(int i=1;i<=n;i++) scanf("%lld",&h[i]);
55     for(int i=1;i<=n;i++) scanf("%lld",&s[i]);
56     for(int i=1;i<=n;i++) pos[i]=i,s[i]+=s[i-1];
57     memset(dp,0x3f,sizeof dp),dp[1]=0;
58     CDQ(1,n),printf("%lld",dp[n]);
59     return 0;
60 }
View Code

T3 UOJ 310 黎明前的巧克力

拿全集作个差就是一道题了

我们要选出异或和相同的集合,等价于选出一个异或和为零的集合,然后拆成两个集合。所以可以设计一个朴素的DP:$dp[i][j]$表示考虑前$i$个数选出异或和为$j$的方案数。进一步我们发现这是在FWT,零的贡献是1,当前卷的数对一些位置贡献2,对一些位置贡献-2。又因为FWT是线性变换,所以最终卷出来是一坨1-2=-1和1+2=3。整体做一次FWT之后,解方程得到每个位置具体的-1和3的个数,然后IFWT回来

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define lli long long
 5 using namespace std;
 6 const int N=1100000,mod=998244353;
 7 int n,m,nm,rd,inv2,inv4;
 8 int bkt[N],res[N],fac[N],inv[N];
 9 void Maxi(int &x,int y){if(x<y) x=y;}
10 int Qpow(int x,int k)
11 {
12     if(k==1) return x;
13     int tmp=Qpow(x,k/2);
14     return k%2?1ll*tmp*tmp%mod*x%mod:1ll*tmp*tmp%mod;
15 }
16 int C(int a,int b)
17 {
18     return 1ll*fac[a]*inv[b]%mod*inv[a-b]%mod;
19 }
20 void Pre()
21 {
22     int lim=1e6; 
23     fac[0]=inv[0]=1,nm=1,inv2=Qpow(2,mod-2),inv4=Qpow(4,mod-2);
24     for(int i=1;i<=lim;i++) fac[i]=1ll*fac[i-1]*i%mod;
25     inv[lim]=pow(fac[lim],mod-2);
26     for(int i=lim-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
27 }
28 void Trans(int *arr,int tot,int typ)
29 {
30     register int i,j,k; 
31     for(i=2;i<=tot;i<<=1)    
32     {
33         int len=i>>1,tmp;
34         for(j=0;j<tot;j+=i)
35             for(k=j;k<j+len;k++)
36                 tmp=arr[k+len],arr[k+len]=(arr[k]-tmp+mod)%mod,arr[k]=(arr[k]+tmp)%mod;
37         if(typ==-1)
38             for(j=0;j<tot;j++)
39                 arr[j]=1ll*arr[j]*inv2%mod;
40     }
41 }
42 int main()
43 {
44     scanf("%d",&n),Pre();
45     for(int i=1;i<=n;i++)
46     {
47         scanf("%d",&rd);
48         bkt[0]++,bkt[rd]+=2,Maxi(m,rd);
49     }
50     while(nm<=m) nm<<=1;
51     Trans(bkt,nm,1); 
52     for(int i=0;i<nm;i++)
53     {
54         int tmp=1ll*(n+bkt[i])*inv4%mod;
55         res[i]=((tmp+n)%mod)%2?mod-Qpow(3,tmp):Qpow(3,tmp);
56     }
57     Trans(res,nm,-1);
58     printf("%lld",1ll*(Qpow(3,n)-res[0]+mod)*Qpow(2,mod-2)%mod);
59     return 0;
60 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/10542430.html