D. Beautiful Graph
题意:给定一张无向无权图,每个顶点可以赋值1,2,3,现要求相邻节点一奇一偶,求符合要求的图的个数。
题解:不联通二分图染色问题
#include <bits/stdc++.h>
using namespace std;
const int N = int(3e5) + 999;
const int MOD = 998244353;
int n, m;
vector <int> g[N];
int p2[N];
int cnt[2];
int col[N];
bool bad;
void dfs(int v, int c){///染色
col[v] = c;
++cnt[c];
for(auto to : g[v]){
if(col[to] == -1) dfs(to, 1 - c);
if((col[v] ^ col[to]) == 0)
bad = true;
}
}
int main() {
p2[0] = 1;
for(int i = 1; i < N; ++i)///二次幂打表
p2[i] = (2 * p2[i - 1]) % MOD;
int tc;
scanf("%d", &tc);
while(tc--){
scanf("%d%d", &n, &m);
for(int i = 0; i < n; ++i)
g[i].clear();
for(int i = 0; i < m; ++i){
int u, v;
scanf("%d %d", &u, &v);
--u, --v;
g[u].push_back(v);
g[v].push_back(u);
}
int res = 1;
for(int i = 0; i < n; ++i) col[i] = -1;
for(int i = 0; i < n; ++i){
if(col[i] != -1) continue;
bad = false;
cnt[0] = cnt[1] = 0;
dfs(i, 0);
if(bad){
puts("0");
break;
}
int cur = (p2[cnt[0]] + p2[cnt[1]]) % MOD;
res = (res * 1LL * cur) % MOD;
}
if(!bad) printf("%d\n", res);
}
return 0;
}
E - Two Arithmetic Progressions
题意:
x
=
a
1
k
’
+
b
1
=
a
2
l
’
+
b
2
x = a1k’ + b1 = a2l’ + b2
x = a1k’ + b1 = a2l’ + b2, 有
L
≤
x
≤
R
L ≤ x ≤ R
L ≤ x ≤ R,且
k
’
,
l
’
≥
0
k’, l’ ≥ 0
k’, l’ ≥ 0。问符合条件 的
x
x
x有多少个。
题解:
优化区间
[
L
,
R
]
[L,R]
[L,R]为
[
m
a
x
(
L
,
m
a
x
(
b
1
,
b
2
)
)
,
R
]
[max(L, max(b1,b2)), R]
[max(L,max(b1,b2)),R]使得等间隔的数都为符合题意的解。
此操作使得满足k,l大于等于0;
然后统一将cur移到区间外计算
[
L
−
1
,
R
]
[L-1,R]
[L−1,R]区间内的等间隔的数,即可求解。
注:等间隔=
g
c
d
(
a
1
,
b
1
)
gcd(a1,b1)
gcd(a1,b1),扩展中国剩余定理的应用范围,不局限于模运算,若余数大于模数也可以使用。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=1005;
int k;
LL c[N],m[N];
bool flag;
LL gcd(LL a,LL b)
{
return b?gcd(b,a%b):a;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b) {x=1;y=0;return a;}
else exgcd(b,a%b,y,x),y-=a/b*x;
}
LL inv(LL a,LL mod)
{
LL x,y;
exgcd(a,mod,x,y);
return (x%mod+mod)%mod;
}
LL exCRT(int k)
{
LL m1,m2,c1,c2,g;
for(int i=2;i<=k;i++)
{
m1=m[i-1],m2=m[i];
c1=c[i-1],c2=c[i];
g=gcd(m1,m2);
if((c2-c1)%g) {flag=0;return -1;}
m[i]=m1*m2/g;
c[i]=inv(m1/g,m2/g)*((c2-c1)/g)%(m2/g)*m1+c1;
c[i]=(c[i]%m[i]+m[i])%m[i];
}
return c[k];
}
int main()
{
k=2;
LL l,r,ans=0;
for(int i=1;i<=k;i++)
scanf("%lld%lld",&m[i],&c[i]);
scanf("%lld%lld",&l,&r);
l = max(max(c[1], c[2]), l);
LL cur=exCRT(k);
if(cur==-1)
{
printf("0\n");
return 0;
}
if(cur>=l){
cur-=((cur-l)/m[2]+1)*m[2];
}
if(l<=r){
ans=(r-cur)/m[2]-(l-1-cur)/m[2];
}
printf("%lld\n", ans);
}
D. Treasure Hunting
题意:
你在一个可以表示为
n
×
m
n×m
n×m表的岛上。行号从1到
n
n
n,列号从1到
m
m
m。岛上有
k
k
k个宝物,第
i
i
i个位于位置
(
r
i
,
c
i
)
(ri,ci)
(ri,ci)。
最初,您站在岛的左下角,位置 ( 1 , 1 ) (1,1) (1,1)。如果任何时候你带着一件宝物在牢房里,你都可以毫不费力地把它捡起来。只需一步,您就可以向上移动(从 ( r , c ) (r,c) (r,c)到 ( r + 1 , c ) (r+1,c) (r+1,c)),向左移动(从 ( r , c ) (r,c) (r,c)到 ( r , c − 1 ) (r,c - 1) (r,c−1)),或者向右移动(从位置 ( r , c ) (r,c) (r,c)到 ( r , c + 1 ) (r,c+1) (r,c+1))。因为陷阱,你不能往下走。
然而,升职也有风险。只有在安全栏中,你才能向上移动。有
q
q
q个安全列:
b
1
、
b
2
、
…
、
b
q
b1、b2、…、bq
b1、b2、…、bq。你想尽快收集所有的财宝。计算收集所有宝物所需的最小移动次数。
题解:
按提议模拟,从下到上每一层记录只需要最左端的宝物位置
l
[
x
]
l[x]
l[x]和最右端的宝物位置
r
[
x
]
r[x]
r[x],因为最小步数必须经过
r
[
x
]
−
l
[
x
]
r[x]-l[x]
r[x]−l[x],所以中间的宝物一定会取到,所以搜集完的最后一个宝物一定是
l
[
x
]
l[x]
l[x]或
r
[
x
]
r[x]
r[x]。
然后考虑在哪一个安全区进入和在哪一个安全区出,如果搜集完所有宝物后,就从就近的安全区出去一定是步数最少的,所以就有从
r
[
x
]
r[x]
r[x]的左出,
r
[
x
]
r[x]
r[x]右出,
l
[
x
]
l[x]
l[x]的左出,
l
[
x
]
l[x]
l[x]右出,共四种状态.
然后进入的位置和出去的位置考虑的方式一样。所以一层一共
16
16
16种状态,共
n
n
n层,枚举一下即可。
O
(
16
∗
n
)
O(16*n)
O(16∗n)的复杂度
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, int> pli;
typedef pair<ll, ll> pll;
typedef long double ld;
const int N=2e5+10;
const int MAXN=20010;
const ll MAX=2e18;
const int INF=0x3f3f3f3f;
const double eps=0.0000001;
const ll mod=1e9+7;
ll n,m,x,y,t,k,p;
ll b[N],l[N],r[N],safe[N];
vector<pll>cup;///存储每一层的开始节点,和这一层的步数
int main()
{
scanf("%lld%lld%lld%lld",&n,&m,&k,&p);
ll top=1;
l[1]=1,r[1]=1;
for(int i=1;i<=k;i++)
{
scanf("%lld%lld",&x,&y);
if(l[x]==0) l[x]=y;
else l[x]=min(y,l[x]);
if(r[x]==0) r[x]=y;
else r[x]=max(y,r[x]);
top=max(top,x);
}
for(int i=0;i<p;i++)
{
scanf("%lld",&b[i]);
}
sort(b,b+p);
cup.push_back(pll(1,0));
for(int i=1;i<top;i++)
{
if(l[i]==0&&r[i]==0) continue;
vector<pll>tmp;///cup的镜象
int p1=lower_bound(b,b+p,l[i])-b;
ll cnt=0;
if(p1<p) safe[cnt++]=b[p1]; ///靠近左端点的右侧安全区
if(p1) safe[cnt++]=b[p1-1];///靠近左端点的左侧安全区
int p2=lower_bound(b,b+p,r[i])-b;
if(p2<p) safe[cnt++]=b[p2]; ///靠近右端点的右侧安全区
if(p2) safe[cnt++]=b[p2-1];///靠近右端点的左侧安全区
sort(safe,safe+cnt);
cnt=unique(safe,safe+cnt)-safe;
for(int j=0;j<cnt;j++)
{
ll ex=safe[j],rex=MAX;
for(int k=0;k<cup.size();k++)
{
ll bx=cup[k].fi,sum=cup[k].se;
if(ex<=bx)
{
if(l[i]<=ex) sum+=(ex-l[i])*2;
if(r[i]>=bx) sum+=(r[i]-bx)*2;
sum+=bx-ex;
}
else
{
if(l[i]<=bx) sum+=(bx-l[i])*2;
if(r[i]>=ex) sum+=(r[i]-ex)*2;
sum+=ex-bx;
}
rex=min(rex,sum);
//printf("%lld ",rex);
}
tmp.push_back(pll(ex,rex));///找到这一层,从ex安全区离开的最小步数
}
cup=tmp;///跟新cup,进行下一轮的判断
}
ll ans=MAX;
for(int k=0;k<cup.size();k++)
{
ll bx=cup[k].fi,sum=cup[k].se;
if(bx<=l[top]&&bx<=r[top])
sum+=r[top]-bx;
else if(bx>=l[top]&&bx<=r[top])
sum+=r[top]-l[top]+min(bx-l[top],r[top]-bx);
else
sum+=bx-l[top];
ans=min(sum,ans);
}
ans=ans+top-1;
printf("%lld",ans);
}