A. Mike and Frog
题目链接
http://codeforces.com/contest/547/problem/A
题目大意
Xaniar初始的值为
h1
,Abol初始的值为
h2
,每一秒钟,两人各自的值会产生如下变化
问最少多少秒后,两人的值各变为 a1,a2 。若无解输出-1
思路
tutorial里的神奇做法虽然非常丽洁,但是不太好懂,下面是我想出的另外一种解法
首先我们求出 t1 秒 h1 变成 a1 , t2 秒 a1 又变回 a1 , t3 秒 h2 变成 a2 , t4 秒 a2 又变回 a2
下面考虑
t1,t2,t3,t4
均存在的情况,那么问题可以变成求方程组
k,l 最小的一组正整数解
移项得
这个式子可以通过扩展欧几里得得到一组解
设 t2k′−t4l′=gcd(t2,t4) ,若 gcd(t3−t1,gcd(t2,t4)) 则无解。若存在解,则可以得到一组可行解 k=t3−t1gcd(t2,t4)k′,l=t3−t1gcd(t2,t4)l′
对于不定方程 ax−by=c 而言,若一组可行解为 (x0,y0) ,则可以推出通解为 (x0+tb,y0+ta),t∈Z ,因为 ax0−by0=ax0+tab−by0−tab=a(x0+tb)−b(y0+ta)
这样我们就能找出一组正整数解 (k,l) 了,然后我们直接暴力得到最小的一组正整数解 (k,l) 即可得到答案。
而对于其他情况(如不存在 t2 ),需要分别进行特判才能得到正确的解
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
typedef long long int LL;
LL MOD,h1,h2,a1,a2,x1,x2,y1,y2;
LL t1,t2,t3,t4;
LL extGCD(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
x=1,y=0;
return a;
}
LL gcd=extGCD(b,a%b,x,y);
LL t=x;
x=y;
y=t-(a/b)*y; //!!!
return gcd;
}
int main()
{
scanf("%I64d",&MOD);
scanf("%I64d%I64d",&h1,&a1);
scanf("%I64d%I64d",&x1,&y1);
scanf("%I64d%I64d",&h2,&a2);
scanf("%I64d%I64d",&x2,&y2);
while(h1!=a1&&t1<MOD+20)
{
t1++;
h1=(h1*x1+y1)%MOD;
}
if(h1!=a1)
{
printf("-1\n");
return 0;
}
while(h2!=a2&&t3<MOD+20) //!!!!
{
t3++;
h2=(h2*x2+y2)%MOD;
}
if(h2!=a2)
{
printf("-1\n");
return 0;
}
if(t1==t3)
{
printf("%I64d\n",t1);
return 0;
}
while((h1!=a1&&t2<MOD+20)||(h1==a1&&!t2))
{
t2++;
h1=(h1*x1+y1)%MOD;
}
while((h2!=a2&&t4<MOD+20)||(h2==a2&&!t4))
{
t4++;
h2=(h2*x2+y2)%MOD;
}
if((t1+t2)==(t3+t4))
{
printf("%I64d\n",t1+t2);
return 0;
}
if(h1!=a1&&h2==a2)
{
if(t1>=t3&&(t1-t3)%t4==0)
{
printf("%I64d\n",t1);
return 0;
}
}
if(h2!=a2&&h1==a1)
{
if(t3>=t1&&(t3-t1)%t2==0)
{
printf("%I64d\n",t3);
return 0;
}
}
if(h1!=a1)
{
printf("-1\n");
return 0;
}
if(h2!=a2)
{
printf("-1\n");
return 0;
}
LL k,l;
LL gcd=extGCD(t2,t4,k,l);
LL a=t2/gcd,b=t4/gcd;
k=k*(t3-t1)/gcd; //!!!
l=l*(t3-t1)/gcd; //!!!
if((t3-t1)%gcd)
{
printf("-1\n");
return 0;
}
l=-l;
if(k<0)
{
LL tmp=(-k)/b+1;
k+=tmp*b;
l+=tmp*a;
}
if(l<0)
{
LL tmp=(-l)/a+1;
k+=tmp*b;
l+=tmp*a;
}
LL ans=k*t2+t1;
while(1)
{
if(l-a>=0&&k-b>=0)
{
l-=a;
k-=b;
ans=min(ans,k*t2+t1);
}
else break;
}
printf("%I64d\n",ans);
return 0;
}
B. Mike and Feet
题目链接
http://codeforces.com/contest/547/problem/B
题目大意
对于
1≤x≤n
,询问长度为
n
的序列
思路
首先我们求出两个数组
L[],R[]
,
L[i],R[i]
分别代表在
ai
左边最近的比
ai
小的元素的下标,在
ai
右边最近的比
ai
小的元素的下标,这可以通过两次
O(n)
做单调栈得到。那么
(L[i],R[i])
区间里最小的元素就是
a[i]
,我们枚举
a[i]
,来更新
x=R[i]−L[i]−1
的答案,但是这样求得的不一定是最大值。假设
ans[i]
表示
x=i
时的答案,很显然
ans[1]>=ans[2]>=ans[3]...>=ans[n]
,因此我们可以从
n
向
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define MAXN 1100000
using namespace std;
int n,a[MAXN],ans[MAXN];
int L[MAXN],R[MAXN];
int stack[MAXN],top=0;
int main()
{
scanf("%d",&n);
fill(L,L+MAXN,0);
fill(R,R+MAXN,n+1); //!!!!
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
while(top&&a[stack[top]]>=a[i]) top--;
if(top) L[i]=stack[top];
stack[++top]=i;
}
top=0;
for(int i=n;i>=1;i--)
{
while(top&&a[stack[top]]>=a[i]) top--;
if(top) R[i]=stack[top];
stack[++top]=i;
}
for(int i=1;i<=n;i++)
{
int len=R[i]-L[i]-1; //!!!!
ans[len]=max(ans[len],a[i]);
}
for(int i=n;i>=1;i--)
ans[i]=max(ans[i],ans[i+1]);
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
C. Mike and Foam
题目链接
http://codeforces.com/contest/547/problem/C
题目大意
给你
n
个数
思路
我们可以维护当前的集合
S
的对应答案
不妨对
x
分解质因数,
这是一个非常简单的容斥,不必过多赘述。因此我们只需要用一个set来维护当前的集合 S ,并用个数组维护当前的集合
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
#define MAXN 510000
using namespace std;
typedef long long int LL;
set<int>bst;
set<int>::iterator it;
LL ans=0;
LL a[MAXN];
LL divisors[20];
int top=0;
LL cnt[1100000]; //cnt[i]=在bst集合里的是数字i的倍数的数字个数
LL stack[1<<8];
int tot=0; //共有tot种可能的质因数组合乘积
void DFS(int pos,int selected,int flag,LL val) //考虑是否选择第pos个质因数,已经选择了selected个质因数,给答案的贡献为flag*xx,要找出是val的倍数的数字个数
{
if(pos>top)
{
if(!selected) return;
ans+=cnt[val]*flag;
stack[++tot]=val;
return;
}
DFS(pos+1,selected,flag,val); //不选第pos个质因数
DFS(pos+1,selected+1,-flag,val*divisors[pos]); //选第pos个质因数
}
void modify(LL x,LL flag)
{
top=tot=0;
LL tmp=x;
for(LL p=2;p*p<=x;p++)
{
if(x%p==0)
{
divisors[++top]=p;
while(x%p==0) x/=p;
}
}
if(x&&x!=1) divisors[++top]=x;
DFS(1,0,flag,1); //!!!!!
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%I64d",&a[i]);
while(q--)
{
int x;
scanf("%d",&x);
//cout<<"size:"<<bst.size()<<endl;
if(a[x]==1)
{
if(bst.count(x))
{
bst.erase(x);
ans-=bst.size();
printf("%I64d\n",ans);
}
else
{
ans+=bst.size();
bst.insert(x);
printf("%I64d\n",ans);
}
}
else
{
if(bst.count(x))
{
bst.erase(x);
modify(a[x],-1);
for(int i=1;i<=tot;i++) cnt[stack[i]]--;
ans-=bst.size();
ans--;
printf("%I64d\n",ans);
}
else
{
ans+=bst.size();
modify(a[x],1);
printf("%I64d\n",ans);
for(int i=1;i<=tot;i++) cnt[stack[i]]++;
bst.insert(x);
}
}
}
return 0;
}
D. Mike and Fish
题目链接
http://codeforces.com/contest/547/problem/D
题目大意
给你 n 个点的坐标,每个点的坐标均为整数,要将所有点染成红蓝两色,并且每一列、每一行上红点的个数与蓝点的个数相差不能超过1,求一组合法染色方案
思路
将每个行标和每个列标均看成是图中的一个结点,对于每个点
显然我们按照行标-列标连边建立的是一个二分图,那么图中奇数度数的点的个数一定是偶数(每次加入一条边,会为所有点的度数和增加2,那么无论如何,所有点的度数和都是偶数,偶数度数点的度数和为偶数,那么奇数度数点的度数和也是偶数,故奇数度数点的个数为偶数),因此我们可以把度数为奇数的点,两两之间依次连无向边,这样每个点的度数就能都变成偶数了。
那么对于每个联通块,我们以一个奇数度数点为起点(若没有,则以偶数度数点为起点),做欧拉回路,显然是存在欧拉回路的,我们对这条路径进行红蓝边交叉染色。
考虑一般情况,之前这个联通块里没有加任何边,则在欧拉回路中,每个点显然出边个数与入边个数相同(即从每个点离开的次数和进入每个点的次数是相同的),而且欧拉回路长度是偶数(无向图
G
是二部图的充分必要条件为
而若之前这个联通块里加了边,则加边后欧拉回路长度依然是偶数,而每个点新连的边最多只有一条,那么每个点连的红边个数和蓝边个数相差一,也满足了题目给出的要求。
代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <set>
#define MAXE 401000
#define MAXV 401000
using namespace std;
int n,degree[MAXV];
struct edge
{
int v,id;
edge(){}
edge(int _v,int _id):v(_v),id(_id){}
}edges[MAXE*2];
bool operator==(edge a,edge b)
{
return a.v==b.v&&a.id==b.id;
}
bool operator<(edge a,edge b)
{
return a.id<b.id;
}
bool operator>(edge a,edge b)
{
return a.id>b.id;
}
set<edge>G[MAXV];
int stack[MAXE*2],top=0;
bool vis[MAXV];
void AddEdge(int U,int V,int ID)
{
G[U].insert(edge(V,ID));
G[V].insert(edge(U,ID));
}
void DFS(int u)
{
vis[u]=true;
while(!G[u].empty())
{
int v=G[u].begin()->v;
int id=G[u].begin()->id;
G[u].erase(edge(v,id));
G[v].erase(edge(u,id));
DFS(v);
stack[++top]=id;
}
}
int oddpoints[MAXV],tot=0;
char ans[MAXV];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
y+=200000; //!!!!!
degree[x]++,degree[y]++;
AddEdge(x,y,i);
AddEdge(y,x,i);
}
for(int i=1;i<=400000;i++)
if(degree[i]&1)
oddpoints[++tot]=i;
for(int i=1;i<=tot;i+=2)
{
AddEdge(oddpoints[i],oddpoints[i+1],0);
AddEdge(oddpoints[i+1],oddpoints[i],0);
}
for(int i=1;i<=tot;i++)
{
if(vis[oddpoints[i]]) continue;
DFS(oddpoints[i]);
bool flag=false;
while(top)
{
flag^=1;
if(stack[top])
ans[stack[top]]=flag?'r':'b';
top--;
}
}
for(int i=1;i<=400000;i++)
{
if(vis[i]) continue;
DFS(i);
bool flag=false;
while(top)
{
flag^=1;
if(stack[top])
ans[stack[top]]=flag?'r':'b';
top--;
}
}
printf("%s\n",ans+1);
return 0;
}