题解
不好意思,今天大凶(虽然洛谷上是小吉)
第一题——吃蛋糕(cake)
【题目描述】
- 给出方程 ,求出非负解的个数( )。
- 这个一看就知道是欧几里得拓展求出一组解,然后求出x最小的非负解和y最小的非负解。
- 假设 的一组解为 ,而原方程可以看做 ,那么最终的解可以写成
- 然后求出两组解,解的个数就是(最大的x-最小的x)/解之间的差。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
}
LL a,b,c,ans;
void ex_gcd(LL &x,LL &y,LL &p,LL a,LL b){
if(b==0){
x=1;y=0;p=a;
return;
}
ex_gcd(x,y,p,b,a%b);
LL t=x;
x=y;
y=t-a/b*y;
}
LL gcd(LL a,LL b){
return (b==0)?a:gcd(b,a%b);
}
LL fm(LL x,LL y, LL MOD){
if(x<=0) x*=-1ll,y*=-1ll;
LL s=0;
while (x){
if(x&1) s=(s+y)%MOD;
y=(y+y)%MOD;
x>>=1ll;
}
return s;
}
int main(){
// fff();
int T;T=read();
while (T--){
scanf("%lld%lld%lld",&a,&b,&c);
ans=0;
LL x,y,p;
ex_gcd(x,y,p,a,b);
if(c%p!=0){
printf("0\n");
continue;
}
a/=p;b/=p;c/=p;
x=fm(x,c,b);y=fm(y,c,a);
x=(x%b+b)%b;
y=(y%a+a)%a;
LL tx=(c-b*y)/a;
printf("%lld\n",(tx-x)/b+1);
}
}
第二题——01串(zero)
【题目描述】
- 给出
。求出在
之间有多少个初始z满足:
- 使得重复上述多次之后满足给定的01字符串。
- 讲道理暴力能拿50分。
- 标程给的是hash+倍增。
- hash用于判断最终生成的子串是否和结果串相等。
- hash还对于倍增的链接有很大的提速效果orz
- 令上面生成的式子的结果为 ,那么很明显 是单向映射的,那我对于每一个z所产生的最终结果就是唯一确定的,那么我只要造出这m个结果然后判断就可以了哈哈哈哈哈哈
- 造的时候可以利用倍增,上一层造好之后,只要将上次的以 作为原始z的那一串和这一串的结尾 的 作为开头的链接起来就可以了。
- orz我知道我写的很乱你还是直接看代码吧….
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define LL long long
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("zero.in","r",stdin);
freopen("zero.out","w",stdout);
}
const int N=1000100;
const int mo1=1e9+7;
const int bas=233;
int A,c,k,m,n;
vector <int> a[N];
int HASH;
int to[N][2],Pow1[N],Pow2[N],now[N];
int Hash[N][2],nowhash[N];
int ans;
char s[N];
void Ch(int &now,int C){
now=1ll*now*Pow1[C]%mo1;
}
void Add(int &now,int A){
now=now+A;
if(now>=mo1)now-=mo1;
}
bool check(int a,int b){
return a==b;
}
int main(){
// fff();
A=read(),c=read(),k=read(),m=read(),n=read();
scanf("%s",s);
Pow1[0]=1;
for(int i=1;i<=n;i++){
Pow1[i]=1ll*Pow1[i-1]*bas%mo1;
}
int M=m>>1;
for (int i=0;i<m;i++){
int z=i;
now[i]=i;
z=((1ll*A*z+c)/k)%m;
to[i][0]=z;
Hash[i][0]=(z>=M)+1;
}
if(n&1){
for (int i=0;i<m;i++){
nowhash[i]=Hash[now[i]][0];
now[i]=to[now[i]][0];
}
}
for (int i=0;i<n;i++) HASH=(1ll*HASH*bas+s[i]-'0'+1)%mo1;
for (int x=1;x<=17;x++){
for (int i=0;i<m;i++){
to[i][x&1]=to[to[i][(x-1)&1]][(x-1)&1];
Hash[i][x&1]=Hash[i][(x-1)&1];
Ch(Hash[i][x&1],1<<(x-1));
Add(Hash[i][x&1],Hash[to[i][(x-1)&1]][(x-1)&1]);
}
if(n&(1<<x)){
for (int i=0;i<m;i++){
Ch(nowhash[i],1<<x);
Add(nowhash[i],Hash[now[i]][x&1]);
now[i]=to[now[i]][x&1];
}
}
}
for (int i=0;i<m;i++){
if(check(nowhash[i],HASH)) ans++;
}
cout<<ans;
}
第三题——没有上司的舞会(dance)(注意:这个不是洛谷的原题orz)
【题目描述】
- 给出一幅有向无环图求出最长反链。
首先需要知道Dilworth定理,最长反链=最小链覆盖(但我不会证明)
那么就变成了一幅图求最小链覆盖,如果把每一个节点先看做一条链,那么每把两个点链接起来,就减少了一个一条链(保证点之间必须有边),那么如果将一个点一分为二,一个是入度,一个是出度,就变成了求最大二分图匹配了。
- OK,看出板子了,现学匈牙利,直接抄起键盘就是干!
#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#define LL long long
using namespace std;
inline int read(){
int X=0,w=0; char c=0;
while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
return w?-X:X;
}
void fff(){
freopen("dance.in","r",stdin);
freopen("dance.out","w",stdout);
}
const int N=10010;
int n,m;
bitset<N> isfa[N];
int que[N];
int ind[N],top[N],cnt;
vector<int>v[N],vb[N];
struct edge{
int u,v,cap;
};
struct Dinic{
int n,s,t,dis[N],cur[N],que[N];
vector<edge>e;vector<int> v[N];
void Init(int n){
this->n=n;e.clear();
}
void AddEdge(int x,int y,int flw){
e.push_back((edge){x,y,flw});
v[x].push_back(e.size()-1);
e.push_back((edge){y,x,0});
v[y].push_back(e.size()-1);
}
int bfs(){
memset(dis,0x3f,sizeof(dis));
int l=1,r=1;que[1]=s;dis[s]=0;
while(l<=r){
int p=que[l++],to,i;
for (int t=0;t<(int)v[p].size();t++)
if(e[i=v[p][t]].cap&&dis[to=e[i].v]>1e9)
dis[to]=dis[p]+1,que[++r]=to;
}
return dis[t]<1e9;
}
int dfs(int p,int a){
if(p==t||!a) return a;
int sf=0,flw;
for (int &i=cur[p],to;i<(int)v[p].size();i++){
edge &E=e[v[p][i]];
if(dis[to=E.v]==dis[p]+1&&(flw=dfs(to,min(a,E.cap)))){
E.cap-=flw;e[v[p][i]^1].cap+=flw;
a-=flw;sf+=flw;
if(!a) break;
}
}
return sf;
}
int dinic(int s,int t){
this->s=s;this->t=t;
int flw=0;
while (bfs()){
memset(cur,0,sizeof(cur));
flw+=dfs(s,1e9);
}
return flw;
}
}sol;
int main(){
// fff();
n=read(),m=read();
for (int i=1;i<=m;i++){
int x,y;
x=read(),y=read();
v[x].push_back(y);ind[y]++;
vb[y].push_back(x);
}
int l=1,r=0;
for (int i=1;i<=n;i++) if(!ind[i]) que[++r]=i;
while (l<=r){
int now=que[l++];top[++cnt]=now;
for(int i=0;i<(int)v[now].size();i++)
if(!--ind[v[now][i]]) que[++r]=v[now][i];
}
for(int i=1;i<=n;i++){
int p=top[i];isfa[p][p]=1;
sol.AddEdge(0,i*2,1);
sol.AddEdge(i*2+1,1,1);
for (int j=0;j<(int)vb[p].size();j++)
isfa[p]|=isfa[vb[p][j]];
for (int j=1;j<=n;j++)
if(isfa[p][j]&&p!=j)
sol.AddEdge(j*2,p*2+1,1);
}
printf("%d\n",n-sol.dinic(0,1));
}