AGC 011

A
单调队列贪心

B
贪心,排序后找最大的i,使得sum[i]*2< a[i+1]

C
用n个点代表行,n个点代表列,对于边(x,y),在行列的x,y之间连边
对于行的每个联通块分开算,对于行的一个联通块,先算其中孤立的点,然后对于列的某个联通块:
手玩后发现如果这个联通块有奇环,在这个行的联通块里就会只有1个联通块,否则会有两个(二分图,分成S,T两个集合,A为行,B为列 the two connected components are SA × SB ∪ TA × TB and SA ×TB ∪TA ×SB)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 410000;

int n,m;
struct edge{int y,nex;}a[maxn]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int fa[maxn],siz[maxn],v[maxn];
int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
int col[maxn];
bool Col(const int x)
{
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y)
    {
        if(col[y]==-1)
        {
            col[y]=!col[x];
            if(!Col(y)) return false;
        }
        if(col[y]==col[x]) return false;
    }
    return true;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
    for(int i=1;i<=m;i++)
    {
        int x,y; scanf("%d%d",&x,&y);
        ins(x,y); ins(y,x);
        int f1=findfa(x),f2=findfa(y);
        if(f1!=f2) fa[f2]=f1,siz[f1]+=siz[f2];
    }
    memset(col,-1,sizeof col);
    int t1=0,t2=0,one=0;
    for(int i=1;i<=n;i++) if(fa[i]==i) 
    {
        if(siz[i]==1) one++;
        else col[i]=0,v[i]=Col(i),(v[i]?++t2:++t1);
    }
    ll ans=0;
    for(int i=1;i<=n;i++) if(fa[i]==i)
    {
        if(siz[i]==1) ans+=(ll)n;
        else if(v[i]) ans+=(ll)(t1+t2*2)+(ll)siz[i]*one;
        else ans+=(ll)(t1+t2)+(ll)siz[i]*one;
    }
    printf("%lld\n",ans);

    return 0;
}

D
令0表示通过状态,1表示回弹状态
手玩得若第1位是1,直接从最左边弹出,第1位变0,否则ai=ai+1取反,an=1,就是去掉第一位,序列取反然后在末尾加上1。
2n步后这个序列会在10101..和0010101..之间循环,模拟前2n步就行了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;

int n,K;
int q[maxn<<2],head,tail,flag;
char str[maxn];

int main()
{
    scanf("%d%d",&n,&K);
    scanf("%s",str);
    head=1,tail=n;
    for(int i=1;i<=n;i++) q[i]=str[i-1]=='A'?1:0;

    int now=0; flag=0;
    for(;now<=2*n;now++)
    {
        if(now==K) break;
        if(q[head]^flag) q[head]=flag;
        else flag=!flag,head++,q[++tail]=!flag;
    }
    if(now<K&&n%2)
    {
        if(q[head]^flag) ++now,q[head]=flag;
        if(now<K&&(K-now)%2) q[head]=!flag;
    }
    for(int i=head;i<=tail;i++) printf("%c",(q[i]^flag)?'A':'B');
    puts("");

    return 0;
}

E
发现每个上升数可以表示成至多9个全1数的和(不考虑进位产生),即如果n能用不超过9k个全1数的和表示,那一定能用不超过k个上升数的和表示
又因为i位全1数可以表示成(10^(i+1)-1)/9,所以有
N=sigmai=1to9k(10(ai+1)1)/9
9N+9k=sigmai=1to9k10(ai+1)
发现右边这个东西,如果合法,其实就是限制了9N+9k所有位上数字的和<=9k
高精度加法

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 610000;

int a[maxn],n,k;
int sum;
void mul()
{
    for(int i=0;i<n;i++) a[i]*=9;
    for(int i=0;i<n;i++) if(a[i]>9)
    {
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    while(a[n]) n++;
    for(int i=0;i<n;i++) sum+=a[i];
}
void add()
{
    a[0]+=9; sum+=9;
    int nowi=0;
    while(a[nowi]>9)
    {
        sum++; a[nowi+1]++;
        sum-=10; a[nowi++]-=10;
    }
    if(a[n]) n++;
}

int main()
{
    char c;
    while((c=getchar())>='0'&&c<='9') a[n++]=c-'0';
    for(int i=0;i<n-i-1;i++) swap(a[i],a[n-i-1]);
    mul();
    for(int k=1;k<=n;k++)
    {
        add();
        if(sum<=9*k) return printf("%d\n",k),0;
    }

    return 0;
}

F
这题做了好久啊…
首先这题是在mod K下的,如果有一条单向路的ai*2>K 答案就为-1
可以发现,对于一种安排方案,我们将他的红线平移,接在一起是不影响答案的
这里写图片描述
->
这里写图片描述

所以最后可以得到这样的东西
这里写图片描述
(画图水平有限,红蓝线交叉的行是双向的道路,单向道路不允许红蓝线在非黑线上交叉
因为每条道路的通过时间一定花费,红线直接贯穿走完,蓝线可以选择等红线过了再走(单项道路),所以就是要求蓝线的最少等待时间
然后根据每一行内,红线经过花费的时间,可以求出蓝线能走的合法的时间区间[Li,Ri] (Mod K下)
这里写图片描述
问题变成了,有一个人,一开始在某个位置,然后他每次可以向右移动任意距离,每次移动后他必须在区间[Li,Ri](Mod K)内,问他最小需要移动的距离
贪心的去做这个问题,每次能不走肯定不走,走的话走到区间的端点,一定是最优的,所以可以dpL表示在Li,经过i~n的区间后最少移动的距离,dpR类似,转移的话,倒着dp,对于Li,找到后面第一个不包含Li的区间,Li到他的左右端点的答案取min
找后面第一个不含pos的,可以用个线段树找,对合法区间的补集区间打个i的tag(可以直接覆盖原来的标记)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

const int maxn = 210000;

int n,K;
int li[maxn],ri[maxn],cnt;
bool if2[maxn];
ll fl[maxn],fr[maxn];
ll re;

struct segment{int lc,rc,c;}seg[maxn*100]; int root,tot;
int lx,rx,c;
void pushdown(const int x)
{
    if(!seg[x].c) return;
    int &lc=seg[x].lc,&rc=seg[x].rc;
    if(!lc) lc=++tot;
    if(!rc) rc=++tot;
    seg[lc].c=seg[x].c;
    seg[rc].c=seg[x].c;
    seg[x].c=0;
}
void upd(int &x,const int l,const int r)
{
    if(rx<l||r<lx) return;
    if(!x) x=++tot;
    if(lx<=l&&r<=rx) { seg[x].c=c; return ; }
    pushdown(x);
    int mid=l+r>>1;
    upd(seg[x].lc,l,mid); upd(seg[x].rc,mid+1,r);
}
int loc;
int query(const int x,const int l,const int r)
{
    if(l==r) return seg[x].c;
    pushdown(x);
    int mid=l+r>>1;
    if(loc<=mid) return query(seg[x].lc,l,mid);
    else return query(seg[x].rc,mid+1,r);
}

int dis(int x,int y) { return x<y?y-x:K-x+y; }
ll cal(int x,int i) { return min((ll)dis(x,li[i])+fl[i],(ll)dis(x,ri[i])+fr[i]); }

int main()
{
    scanf("%d%d",&n,&K); ll sum=0;
    for(int i=1;i<=n;i++) 
    {
        int x,y; scanf("%d%d",&x,&y); re+=(ll)x<<1;
        sum=(sum+(ll)x)%K;
        li[++cnt]=sum,ri[cnt]=x;
        if(y==1)
        {
            if(x*2>K) return puts("-1"),0;
        }
        else if2[cnt]=true;
    }
    for(int i=cnt,las=0;i>=1;i--)
    {
        int templ=(li[i]-las+K)%K,tempr=((ll)li[i-1]-ri[i]-las+(ll)K*2)%K;
        if(if2[i]) templ=0,tempr=K-1;
        las=(las+ri[i])%K;
        li[i]=templ,ri[i]=tempr;
    }
    for(int i=1;i*2<=cnt;i++) swap(li[i],li[cnt-i+1]),swap(ri[i],ri[cnt-i+1]);
    seg[root=++tot].c=cnt+1;
    for(int i=cnt;i>=1;i--)
    {
        int tmp;
        loc=ri[i]; tmp=query(1,0,K-1);
        fr[i]=tmp==n+1?0:cal(ri[i],tmp);

        loc=li[i]; tmp=query(1,0,K-1);
        fl[i]=tmp==n+1?0:cal(li[i],tmp);

        c=i;
        if(li[i]<=ri[i])
        {
            if(li[i]) lx=0,rx=li[i]-1,upd(root,0,K-1);
            if(ri[i]<K-1) lx=ri[i]+1,rx=K-1,upd(root,0,K-1);
        }
        else if(li[i]-1>ri[i])
        {
            lx=ri[i]+1,rx=li[i]-1,upd(root,0,K-1);
        }
    }
    seg[1].c=-1; ll ans=LLONG_MAX;
    for(int i=1;i<=cnt;i++)
    {
        loc=li[i]; if(query(1,0,K-1)==-1) ans=min(ans,fl[i]);
        loc=ri[i]; if(query(1,0,K-1)==-1) ans=min(ans,fr[i]);

        c=i;
        if(li[i]<=ri[i])
        {
            if(li[i]) lx=0,rx=li[i]-1,upd(root,0,K-1);
            if(ri[i]<K-1) lx=ri[i]+1,rx=K-1,upd(root,0,K-1);
        }
        else if(li[i]-1>ri[i])
        {
            lx=ri[i]+1,rx=li[i]-1,upd(root,0,K-1);
        }
    }
    printf("%lld\n",re+ans);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值