第一次套刷AtCoderios
体验良好git
传送门数组
cout<<b+min(c,a+b+1);
难度跨度有点大啊优化
能够证实当第一次转向以后,接下来每次的方向都和前一次相反spa
由于转向后再往相同方向走必定不如初始就往该方向走而后转两次向code
枚举初始往哪一个方向走以及走几步,前缀和优化便可blog
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans;ll ret; int a[200010]; ll hz[200010],qz[200010],qzh[200010]; void calc(){ for(rt i=n-1;i>=1;i--)hz[i]=hz[i+1]+2ll*(n-i)*a[i]; qz[1]=qzh[1]=a[1]; for(rt i=2;i<=n;i++)qz[i]=qz[i-1]+(2ll*i-1)*a[i],qzh[i]=qzh[i-1]+a[i]; ll ans=0; for(rt i=1;i<=n;i++){ ans+=a[i];int md=i+n>>1; ret=max(ret,ans-hz[md+1]-(qz[md]-qz[i]-(qzh[md]-qzh[i])*2ll*i)+qzh[n]*(n-i-1)); } } int main(){ int L=read();n=read();a[++n]=L; for(rt i=1;i<n;i++)a[i]=read(); for(rt i=n;i>=2;i--)a[i]-=a[i-1]; calc();reverse(a+1,a+n+1);calc(); cout<<ret; return 0; }
清真构造题继承
若$ k\leq n$显然能够构一个$ k*k$的正方形,第$ i$行全填$ i$便可get
当$ n < k \leq 2n$时,尝试从斜向入手string
发现每一个斜行能够交错填写数字,这样能填写的数字就能达到$ 2n$级别了
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define inv2 500000004 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans[505][505]; int main(){ k=read(); if(k<=500){ writeln(k); for(rt i=1;i<=k;i++)for(rt j=1;j<=k;j++)cout<<i<<" \n"[j==k]; return 0; } writeln(n=500); for(rt i=1;i<=n;i++){ ans[1][i]=i; for(rt x=n,y=i%n+1;x!=1;x--,y=y%n+1)ans[x][y]=i; } for(rt i=n+1;i<=k;i++){ if(i+i&1)ans[1][i]=i; for(rt x=n,y=i%n+1;x!=1;x--,y=y%n+1)if(i+y&1)ans[x][y]=i; } for(rt i=1;i<=n;i++)for(rt j=1;j<=n;j++)cout<<ans[i][j]<<" \n"[j==n]; return 0; }
比B和C都清真多了...
设$ f[i][j][k]$表示通过$ i$次操做,$ a_j>a_k$的方案数
容易发现每次操做只对$ O(n)$级别的状态进行了非乘2的修改,其余都只是继承状态而后$ *2$
不妨把状态改为通过$ i$次操做,每次操做有$ 50\%$几率交换两个数,求逆序对的指望
滚动数组以后能够$ O(n^2)$解决转移,最后乘上$ 2^q$便可
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define inv2 500000004 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; int f[3010][3010],g[3010][3010],a[3010]; int main(){ n=read();m=read(); for(rt i=1;i<=n;i++)a[i]=read(); for(rt i=1;i<=n;i++) for(rt j=1;j<=n;j++)if(a[i]>a[j])f[i][j]=1; for(rt i=1;i<=m;i++){ x=read();y=read(); for(rt j=1;j<=n;j++)g[x][j]=f[x][j],g[j][x]=f[j][x],g[y][j]=f[y][j],g[j][y]=f[j][y]; f[x][y]=f[y][x]=1ll*(g[x][y]+g[y][x])*inv2%p; for(rt j=1;j<=n;j++){ const int v1=1ll*(g[x][j]+g[y][j])*inv2%p,v2=1ll*(g[j][x]+g[j][y])*inv2%p; if(j!=y&&j!=x)f[x][j]=v1,f[j][x]=v2; if(j!=x&&j!=y)f[y][j]=v1,f[j][y]=v2; } } int ans=0; for(rt i=1;i<n;i++) for(rt j=i+1;j<=n;j++)(ans+=f[i][j])%=p; for(rt i=1;i<=m;i++)ans=ans*2%p; cout<<ans; return 0; }
神仙题!
咱们在两个01串中的全部“01”结构之间连一条红线,“10”结构之间连一条蓝线
特判$ n \leq 2$以后发现两个01串等价的充要条件是这两个01串的红蓝线一一对应
发现每次改变非边界的数只会致使一条红/蓝线左移一位或右移一位
改变边界上的数可能会致使生成一条新的红/蓝线或使一条红/蓝线消失不见
容易发现移动不能使同一位置有多条线且线的颜色始终保持红蓝对应
咱们能够把边界理解为有无限多条颜色交错的红蓝线
枚举上面的每条红线对应下面的每条红线,红线对答案的贡献就是每组相互对应的线的距离和 蓝线同理
数据范围容许暴力枚举对应$ O(n^2)$经过此题
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans,ret; int a[100010],b[100010],t1,t2; char c[100010],s[100010]; int calc(){ t1=t2=0; for(rt i=1;i<=n;i++)b[++t2]=0; for(rt i=1;i<n;i++)if(c[i]=='0'&&c[i+1]=='1')a[++t1]=i; for(rt i=1;i<n;i++)if(s[i]=='0'&&s[i+1]=='1')b[++t2]=i; for(rt i=1;i<=n;i++)b[++t2]=n;int nowmin=998244353; for(rt i=1;i<=t2-n+1;i++){ int ans=0; for(rt j=1;j<i;j++)if(b[j])ans+=b[j]; for(rt j=0;j<t1;j++)ans+=abs(a[j+1]-b[i+j]); for(rt j=t1;b[i+j]!=n;j++)ans+=n-b[i+j]; nowmin=min(nowmin,ans); } return nowmin; } int main(){ n=read(); scanf("%s",c+1);scanf("%s",s+1); if(n==1&&c[1]!=s[1])writeln(1); else if(n==2&&c[1]!=s[1]&&c[1]==c[2]&&s[1]==s[2])writeln(2); else { ret=calc(); for(rt i=1;i<=n;i++)c[i]='0'+'1'-c[i],s[i]='0'+'1'-s[i]; ret+=calc();cout<<ret; } return 0; }
正推推不出来的DP题启示咱们弃疗倒着推
显然两个位置若是都不是$ -1$就能够扔掉
设有cnt组两个位置都是$ -1$则最终答案须要乘上$ cnt!$由于这些组能够任意交换位置
设$ f[i][j][k]$表示已经填了不小于$ i$的全部数,有$ j$个在序列中出现过的数没有被匹配,有$ k$个在原数列中未出现过的数没有被匹配
假设当前数在原序列出现过则能够匹配一个未出现过的数或本身变成一个未被匹配的数
不能匹配一个出现过的数的缘由是假设相邻两个数都不是-1则要被扔掉
假设当前数在原序列中未出现过则能够匹配一个未出现过的数,一个出现过的数或本身成为未被匹配的数
注意若是匹配一个出现过的数有$ j$种匹配方式
直接$ O(n^3)DP$便可
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; int a[605];bool vis[605],nt[605]; int q[605],t; int f[605][305][305]; int main(){ n=read(); for(rt i=1;i<=2*n;i++){ a[i]=read();if(a[i]!=-1)vis[a[i]]=1; if(i&1^1){ if(a[i]==-1&&a[i-1]==-1)cnt++; if(a[i]!=-1&&a[i-1]!=-1)nt[a[i]]=nt[a[i-1]]=1; } } for(rt i=1;i<=2*n;i++)if(!nt[i])if(vis[i])q[++t]=1;else q[++t]=0;n=t; //q=1在原数列中 f[n][0][0]=1; for(rt i=n;i>=1;i--){ for(rt j=0;j<=n/2;j++) for(rt k=0;k<=n/2;k++)if(f[i][j][k]){ const int v=f[i][j][k]; if(q[i]){ (f[i-1][j+1][k]+=v)%=p; if(k)(f[i-1][j][k-1]+=v)%=p; }else { (f[i-1][j][k+1]+=v)%=p; if(k)(f[i-1][j][k-1]+=v)%=p; if(j)(f[i-1][j-1][k]+=1ll*v*j%p)%=p; } } } int ans=f[0][0][0]; for(rt i=2;i<=cnt;i++)ans=1ll*ans*i%p; cout<<ans; return 0; }