AB省略
C - 3 Steps
若u可以走3条边到v,则可以连边(u,v),发现就是将(u,v)间的距离由3变成1,同时所有经过u,v的路径长度减少了2,但奇偶性都没有改变,可以推得,最后u将会和所有它能走奇数距离到达的点连边
如果原图有奇环,u能够通过奇环调整,使得它与所有点连边
如果不存在奇环即是一个二分图,不同颜色的点对之间都会有边
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n,m;
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}
int col[maxn],N[2];
bool color(const int x)
{
bool re=true;
for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
{
if(col[y]==-1) N[col[y]=!col[x]]++,re=color(y);
else if(col[y]!=!col[x]) re=false;
if(!re) return re;
}
return re;
}
int main()
{
memset(col,-1,sizeof col);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y; scanf("%d%d",&x,&y);
ins(x,y); ins(y,x);
}
N[col[1]=0]++; bool flag=color(1);
if(flag) printf("%lld\n",(ll)N[0]*N[1]-m);
else printf("%lld\n",(ll)n*(n-1)/2ll-m);
return 0;
}
D - 101 to 010
发现相隔超过1个0后两段答案互不影响,相互隔绝
对于一段内,一定是形如1111..011..01..的形式
同时发现101->010的变换,如果某一边的1另一边是0,合并完后就形成隔绝了
所以对于一段1,左边合并上来后就与左边的隔绝了,一定是一直合并完或者合并剩最右边的1(右边同理)。
注意到变换只和左1位是不是0,左2位是不是1有关(右同理),可以dp[i][0/1/2]表示处理了i前面(包含i)的合并,i前面第一个1离i的距离(>=2的都视作2)的最大次数……..然后我狂wa不止…转移的细节有点多…
然而好像直接dp[i]就可以做了,细节还少很多..
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;
inline void up(int &x,const int &y){if(x<y)x=y;}
const int maxn = 510000;
int n;
char str[maxn];
int f[maxn][3],g[maxn];
int solve(const int l,const int r)
{
f[l-1][2]=0; g[l-1]=0;
for(int i=l;i<=r;i++)
{
int gg=g[i-1]; up(g[i-1],f[i][2]);
if(str[i]=='0')
{
up(f[i][1],f[i-1][0]);
up(f[i][2],max(f[i-1][1],f[i-1][2]));
}
else
{
int j=i+1; for(;j<=r&&str[j]=='1';j++);j--;
if(j!=i)
{
up(f[j][0],max(g[i-1],f[i-1][1]+j-i));
up(f[j][1],f[i-1][1]+j-i+1);
up(f[j][2],g[i-1]);
if(j!=r) up(f[j+2][2],max(gg+j-i+1,g[i-1]+j-i));
i=j;
}
else
{
up(f[i][0],gg);
up(f[i][1],f[i-1][1]+1);
up(f[i][2],g[i-1]);
}
}
for(int j=0;j<3;j++) up(g[i],f[i][j]);
}
return max(g[r],0);
}
int main()
{
scanf("%d",&n); scanf("%s",str+1);
for(int i=0;i<=n;i++) for(int j=0;j<3;j++) f[i][j]=-inf;
int ans=0;
for(int i=1;i<=n;i++) if(str[i]=='1')
{
int j=i+1;
for(int las=i;j<=n;j++)
{
if(str[j]=='1') las=j;
if(j-las>=2) break;
}j--;
for(;str[j]=='0';j--);
ans+=solve(i,j); i=j;
}
printf("%d\n",ans);
return 0;
}
E - Popping Balls
懒得贴图了,你们看题解的图凑合吧qwq
将问题变成二维平面上,从点(A,B)到(0,0)的不同路径数,满足要求:任何时候都可以左移,只有x< s,x+y>=s或x< t,x+y>=t时才能向下移(相当于竖边只能在两个梯形内才能往下走)
对于计算答案,从(A,B)开始往左走,枚举第一次往下走的横坐标i,此时第二个梯形的底边为x=i时显然能最大化方案数,再枚举走到这个梯形斜边的哪个位置(a,i-a),从(i,m-1)到(a,i-a)的路径没有限制,可以直接组合数,从(a,i-a)到(0,0)的合法路径数可以仿照上述过程,做个dp求
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const ll Mod = 1e9+7;
const int maxn = 2100;
int n,m;
ll c[maxn][maxn],pc[maxn][maxn];
ll f[maxn][maxn],g[maxn][maxn];
int main()
{
scanf("%d%d",&n,&m);
c[0][0]=1ll; pc[0][0]=1ll;
for(int i=1;i<maxn;i++)
{
c[i][0]=1ll; pc[i][0]=1ll;
for(int j=1;j<=i;j++)
c[i][j]=(c[i-1][j-1]+c[i-1][j])%Mod,
pc[i][j]=(pc[i][j-1]+c[i][j])%Mod;
}
for(int i=0;i<=n;i++) f[i][0]=g[i][0]=1ll;
for(int i=0;i<=n;i++)
{
for(int j=1;j<=m;j++)
f[i][j]=pc[j-1][min(i,j-1)];
}
for(int j=1;j<=m;j++)
{
g[0][j]=f[0][j];
for(int i=1;i<=n;i++) g[i][j]=(g[i-1][j]+f[i][j])%Mod;
}
ll ans=1;
for(int i=n;i>=1;i--)
{
for(int y=0,u=min(m-1,i);y<=u;y++)
{
int x=i-y; //(i,m-1) -> (x,y)
ans+=g[x][y]*c[i+m-1-x-y][i-x]%Mod;
}
}
ans%=Mod;
printf("%lld\n",ans);
return 0;
}
F - Largest Smallest Cyclic Shift
可能因为语言障碍?不知道题解在说什么…..然后膜了一个很神奇的代码
先将X个a,Y个b,Z个c放进multiset里面,每次取出最小和最大的字符串,将最大的接在最小的后面,塞回multiset,直到multiset里面只剩1个串,就是答案。
个人yy了一个很(不)严谨的证明:
用数学归纳法,假设每一步前都保证目标串由当前这些字符串组成,证明每一步操作后目标串仍由这些串组成
因为要最小表示最大,当前最小的字符串可以视为是目标串的开头,当然要让他尽量大,所以给他拼上当前最大的字符串,符合题目要求,所以目标串仍然由这些串组成 证明默认了最小的串唯一(唯一的情况证了,不唯一的情况类似,意会一下,结论仍然成立)
然后因为一开始每个串长度都是1的情况显然符合目标串由这些串组成,所以最后拼成的串就是目标串
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int x,y,z;
multiset<string>S;
multiset<string>::iterator it;
int main()
{
scanf("%d%d%d",&x,&y,&z);
for(int i=1;i<=x;i++) S.insert("a");
for(int i=1;i<=y;i++) S.insert("b");
for(int i=1;i<=z;i++) S.insert("c");
while(S.size()>1)
{
it=S.end(); it--;
string ss=(*S.begin())+(*it);
S.erase(S.begin());
S.erase(it);
S.insert(ss);
}
cout<<(*S.begin())<<endl;
return 0;
}