根据难度排序。
可能是最容易看懂得题解?
个人总结向。
如果有什么讲的不清楚的欢迎留言私信交流~
题意: 给出两个无限循环串的循环节,比较两个串的大小。
思路:
假设一个字符串 S 有循环节(不需要是完整循环节) p 和 q ,并且满足 p + q ≤ S + g c d ( p , q ) p+q≤S+gcd(p,q) p+q≤S+gcd(p,q),那么 g c d ( p , q ) gcd(p,q) gcd(p,q) 也是一个循环节。
证明点这里
题解中说到只要复制到长度为(n+m)即可,也就是说可以记住这个结论。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;
int main()
{
string a,b;
while(cin>>a>>b){
string s1=a+b,s2=b+a;
if(s1<s2) cout<<"<"<<endl;
else if(s1==s2) cout<<"="<<endl;
else cout<<">"<<endl;
}
return 0;
}
题意: 求 ∫ 0 1 ( x − x 2 ) n d x ∫_{0}^{1}(x−x^{2})^{n}dx ∫01(x−x2)ndx 输出分数 p / q p/q p/q为 p ∗ q − 1 p∗q^{-1}%998244353 p∗q−1
思路:
欧拉积分:
伽玛函数: Γ ( n + 1 ) = n ! Γ(n+1) = n! Γ(n+1)=n!
贝塔函数: B ( p , q ) = ∫ 0 1 x p − 1 ∗ ( 1 − x ) q − 1 d x = Γ ( p ) ∗ Γ ( q ) / Γ ( q + p ) B(p,q)=∫_{0}^{1}x^{p-1}*(1-x)^{q-1}dx=Γ(p)*Γ(q)/Γ(q+p) B(p,q)=∫01xp−1∗(1−x)q−1dx=Γ(p)∗Γ(q)/Γ(q+p)
过程: ∫ 0 1 x n ∗ ( 1 − x ) n d x = B ( n + 1 , n + 1 ) = n ! ∗ n ! / ( 2 ∗ n + 1 ) ! ∫_{0}^{1}x^{n}*(1-x)^{n}dx=B(n+1,n+1)=n!*n!/(2*n+1)! ∫01xn∗(1−x)ndx=B(n+1,n+1)=n!∗n!/(2∗n+1)!
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=2e6+5;
const int N=1e3+5;
const int mod=998244353;
ll f[manx],n;
ll qp(ll a, ll b){
ll ans=1;
while(b){
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans%mod;
}
ll inv(ll x){
return qp(x,mod-2);
}
int main()
{
io;
f[1]=1;
for(int i=2;i<=2000005;i++) f[i]=f[i-1]*i%mod;
while(cin>>n){
ll ans=f[n]*f[n]%mod*inv(f[2*n+1])%mod;
cout<<ans<<endl;
}
return 0;
}
题意: 给定网络的边和费用,每次询问给定所有边的容量u和要求的流量v,问把一单位流量从1 流向 n 最少所需要的费用,如果不能满流输出NaN。
思路:
很明显每条边的容量都是相同的,所以我们可以初始化为1,这样做的好处是 m a x f l o w ∗ ( u / v ) maxflow * (u/v) maxflow∗(u/v) 就是当前询问的最大流,在增广路径的时候用 map 记录每一条路径的流量与花费(map会自动排序)。
如果 m a x f l o w ∗ ( u / v ) < 1 maxflow * (u/v) < 1 maxflow∗(u/v)<1 的话,则输出NaN 。
否则的话,我们不断地加入每一条路径,并且累加费用,直到流量不小于 v 时跳出循环,最后总的费用输出时分子分母同时除以分子分母的GCD即可。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=2e3+5;
const int N=1e3+5;
const int mod=1e9+7;
bool vis[manx];
ll n,m,s,t,x,y,z,f,maxflow,mincost,cnt;
ll dis[manx],pre[manx],last[manx],flow[manx],head[manx];
map<ll,ll>mps;
struct node{
ll v,next,flow,dis;
}a[manx];
inline void add(ll u,ll v,ll flow,ll dis){
a[++cnt].next=head[u],a[cnt].v=v,a[cnt].flow=flow,a[cnt].dis=dis,head[u]=cnt;
}
bool spfa(ll s,ll t)
{
for(int i=1;i<=n;i++) vis[i]=0,dis[i]=flow[i]=inf;
queue<ll>q;
q.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;
while(!q.empty()){
ll now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i!=-1;i=a[i].next){
if(a[i].flow>0&&dis[a[i].v]>dis[now]+a[i].dis){
dis[a[i].v]=dis[now]+a[i].dis;
pre[a[i].v]=now;
last[a[i].v]=i;
flow[a[i].v]=min(flow[now],a[i].flow);
if(!vis[a[i].v]){
vis[a[i].v]=1;
q.push(a[i].v);
}
}
}
}
return pre[t]!=-1;
}
inline void donic()
{
while(spfa(s,t)){
ll now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
mps[flow[t]*dis[t]]+=flow[t];
while(now!=s){
a[last[now]].flow-=flow[t];
a[last[now]^1].flow+=flow[t];
now=pre[now];
}
}
}
inline void init(){
for(int i=1;i<=n;i++) head[i]=-1,last[i]=0;
cnt=-1; maxflow=mincost=0; s=1,t=n;
mps.clear();
}
int main()
{
while(scanf("%lld%lld",&n,&m)!=EOF){
init();
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&x,&y,&f);
add(x,y,1,f); add(y,x,0,-f);
}
donic();
ll q; cin>>q;
while(q--){
ll u,v; scanf("%lld%lld",&u,&v);
if(maxflow*u<v) printf("NaN\n");
else{
x=0,y=v;
for(auto sb: mps){
if(v>sb.se*u) v-=sb.se*u,x+=sb.fi*u;
else {
x+=sb.fi/sb.se*v; break;
}
}
z=__gcd(x,y);
printf("%lld/%lld\n",x/z,y/z);
}
}
}
return 0;
}
题意: 给出一张图和每个点的期望度数,要求你删掉一些边使得每个点的度数都等于期望度数,问是否可行。
思路:
在这之前只知道匈牙利算法,现在了解到了一般图最大匹配的解决算法。
以知道这个算法为前提,难点在于想到拆点拆边。(当时我就不知道)
对于每个点要求的度数 d [ i ] d[i] d[i] ,我们将点 i 拆成 d [ i ] d[i] d[i]个
对于每条边 e ( u , v ) e(u,v) e(u,v),我们将 ( u , v ) (u,v) (u,v)拆成,x,y,x与分别与 u 拆成的 d [ x ] d[x] d[x] 个点连边,y点同理,最后再将 ( x , y ) (x,y) (x,y)连一条边(这样做是可以防止 u 或者 v 要求的度数为0的情况,此时直接在 x 与 y 之间连一条边就可以解决)
例如,题目中第三组样例:
3 2
1 1 2
1 3
2 3
第1个点拆成1个点,编号为1。
第2个点拆成1个点,编号为2。
第3个点拆成2个点,编号分别为3,4
第1条边拆成两个点,编号为5,6:(5,6)(5,1)(6,3)(6,4)连边。
第2条边拆成两个点,编号为7,8:(7,8)(7,2)(8,3)(8,4)连边。
注意在这题里是无向边。
代码:
#include
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
const int manx=2e3+5;
const int N=1e3+5;
const int mod=1e9+7;
deque<ll>q;
bool g[N][N],inque[N],inblossom[N];
ll match[N],pre[N],base[N];
inline ll findancestor(ll u,ll v){ //找公共祖先
bool inpath[N]={false};
while(1){
u=base[u];
inpath[u]=true;
if(match[u]==-1)break;
u=pre[match[u]];
}
while(1){
v=base[v];
if(inpath[v])return v;
v=pre[match[v]];
}
}
inline void reset(ll u,ll anc){ //压缩花
while(u!=anc)
{
ll v=match[u];
inblossom[base[u]]=1;
inblossom[base[v]]=1;
v=pre[v];
if(base[v]!=anc)pre[v]=match[u];
u=v;
}
}
inline void contract(ll u,ll v,ll n){
ll anc=findancestor(u,v);
memset(inblossom,0,sizeof(inblossom));
reset(u,anc);
reset(v,anc);
if(base[u]!=anc)pre[u]=v;
if(base[v]!=anc)pre[v]=u;
for(int i=1;i<=n;i++)
if(inblossom[base[i]]){
base[i]=anc;
if(!inque[i]) q.push_back(i),inque[i]=1;
}
}
inline bool dfs(ll S,ll n)
{
for(int i=0;i<=n;i++) pre[i]=-1,inque[i]=0,base[i]=i;
q.clear(); q.push_back(S); inque[S]=1;
while(!q.empty()){
ll u=q.front(); q.pop_front();
for(int v=1;v<=n;v++){
if(g[u][v]&&base[v]!=base[u]&&match[u]!=v){
if(v==S||(match[v]!=-1&&pre[match[v]]!=-1)) contract(u,v,n);
else if(pre[v]==-1){
pre[v]=u;
if(match[v]!=-1) q.push_back(match[v]),inque[match[v]]=1;
else{
u=v;
while(u!=-1){
v=pre[u];
ll w=match[v];
match[u]=v;
match[v]=u;
u=w;
}
return true;
}
}
}
}
}
return false;
}
inline int solve(ll n){
memset(match,-1,sizeof(match));
ll ans=0;
for(int i=1; i<=n; i++)
if(match[i]==-1&&dfs(i,n))
ans++;
return ans;
}
//以上是带花树模板
//g[i][j]存放关系图:i,j是否有边,match[i]存放i所匹配的点
//建图开始初始化g
//最终匹配方案为match
//复杂度O(n^3)
//点是从1到n的
//最终带花树solve给出的是匹配对数
ll d[60];
ll x[210],y[210];
ll mps[55][210];
inline bool build(ll n,ll m)
{
ll num=0;
//拆点,给i点的第j个度一个标号,方便接下来的建图
for(int i=1;i<=n;i++)
for(int j=1;j<=d[i];j++)
mps[i][j]=++num;
//拆边,把边被拆成的两个点分别与两头的每个度相连
for(int i=1;i<=m;i++){
for(int j=1;j<=d[x[i]];j++) g[mps[x[i]][j]][num+1]=g[num+1][mps[x[i]][j]]=1;
for(int j=1;j<=d[y[i]];j++) g[mps[y[i]][j]][num+2]=g[num+2][mps[y[i]][j]]=1;
g[num+1][num+2]=g[num+2][num+1]=1;
num+=2;
}
if(solve(num)*2==num) return 1;
return 0;
}
int main()
{
ll n,m;
while(scanf("%lld%lld",&n,&m)!=EOF){
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++) scanf("%lld",&d[i]);
for(int i=1;i<=m;i++) scanf("%lld%lld",&x[i],&y[i]);
if(build(n,m)) puts("Yes");
else puts("No");
}
return 0;
}