暑假集训日记——7.23(codeforce)

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 LxR,且 k ’ ,   l ’   ≥   0 k’, l’ ≥ 0 k,l0。问符合条件 的 x x x有多少个。
题解:
优化区间 [ L , R ] [L,R] [LR] [ 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] [L1R]区间内的等间隔的数,即可求解。
注:等间隔= g c d ( a 1 , b 1 ) gcd(a1,b1) gcda1,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,c1)),或者向右移动(从位置 ( 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 b1b2bq。你想尽快收集所有的财宝。计算收集所有宝物所需的最小移动次数。
题解:
按提议模拟,从下到上每一层记录只需要最左端的宝物位置 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(16n)的复杂度

#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);
 
 
 
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值