Codeforces 1166

1166 D

题意

我们说一个序列 \(x\) (长为 \(n\) )是 \(m-cute\) 的当且仅当 \(x_i = x_{i - 1} + x_{i - 2} + \dots + x_1 + r_i (1 \le r_i \le m)\) 。现在给定 \(x\) 的首项和末项以及 \(m\) ,问是否存在合法的 \(x\) 。存在还需要构造一个。 \((x_1,x_n,m\le 10^{14})\)

Example

input
2
5 26 2
3 9 1
output
4 5 6 13 26
-1

先假定所有的 \(r_i=1\) ,算出 \(n\) ,然后再从左往右一个个往上加

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=53;
long long a[maxn],c[maxn],s,t,m;
int n;
void calcn(){
    n=2;
    long long sum=s;
    a[1]=s;
    while(1){
        a[n]=sum+1;
        if(a[n]>t)break;
        sum+=a[n];
        n++;
    }
    n--;
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%lld%lld%lld",&s,&t,&m);
        calcn();
        for(int i=1;i<=n;i++)c[i]=0;
        long long x=t-a[n],y,k;
        for(int i=2;i<=n&&x>0;i++){
            k=(i==n?1:1ll<<(n-i-1));
            y=min(m-1,x/k);
            x-=y*k;
            c[i]+=y;
        }
        long long sum=s;
        a[1]=s;
        for(int i=2;i<=n;i++){
            a[i]=sum+1+c[i];
            sum+=a[i];
        }
        if(a[n]==t){
            printf("%d ",n);
            for(int i=1;i<=n;i++)printf("%lld ",a[i]);
            puts("");
        }
        else puts("-1");
    }
    return 0;
}

1166 E

题意

\(m\) 个数(未知),有 \(n\) 天,一个人每天会选择这些数的一个子集 \(D_i\) ,该子集的补集记为 \(S_i\) ,现在要你是否存在这 \(m\) 个数,使得对于每个 \(i\)\(\text{lcm}(D_i)>\text{lcm}(S_i)\)\((n\le 10^4,m\le 50)\)

Examples

input
2 5
3 1 2 3
3 3 4 5
output
possible
input
10 10
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
output
impossible

反正法。
假设 \(\exists i,j,D_i∩D_j=∅\) ,那么有 \(D_i⊆S_j,D_j⊆S_i\) ,则 \(\text{lcm}(D_i)\le \text{lcm}(S_j),\text{lcm}(D_j)\le \text{lcm}(S_i)\) 。所以 \[\text{lcm}(D_j)>\text{lcm}(S_j)≥\text{lcm}(D_i)>\text{lcm}(S_i)≥\text{lcm}(D_j)\]
矛盾,所以如果要possible就必须满足对于 \(∀i,j,D_i∩D_j≠∅\) 。也就是说任意两个 \(D_i\) 有交集。 \(O(nm^2)\) 暴力判断。

1166 F

题意

一张图, \(n\) 个点 \(m\) 条边,现在有 \(q\) 个操作,有两种:

  • \(x,y\) 间连一条颜色为 \(w\) 的边。
  • 询问从 \(x\)\(y\) 是否存在彩虹路径。

彩虹路径:
对于该路径经过的所有的节点数组 \(c\) ,满足对于每个 \(1\le i\le \frac{k-1}{2}\) ,满足边 \((c_{2i},c_{2i-1})\)\((c_{2i},c_{2i+1})\) 颜色相同。

Example

input

4 3 2 4
1 2 1
2 3 1
3 4 2
? 1 4
? 4 1
+ 3 1 2
? 4 1

output

Yes
No
Yes

显然最开始的 \(m\) 条边可以看成加边操作。
新建一张图。对于原图节点 \(x,y,z\) ,如果 \((x,y)\)\((x,z)\) 颜色相同,那么新图中连边 \((y,z)\)
所以对于询问,如果彩虹路径是经过偶数条边的,我们只需要判断两个节点在新图中是否处在同一个连通块内,用并查集维护。
但如果经过奇数条边,最后一条边的颜色就是任意的。因此对于每个节点维护一个set,表示新图中有多少个连通块与之相邻。
加边操作其实就是检查有没有两条相邻的且颜色相同的两条边,如果有,合并两个连通块。
加边的时候为了快速找到有没有上述条件的边,对每个节点再维护一个map, \(map[u][w]\) 表示最新加入的与节点 \(u\) 相邻的、颜色为 \(w\) 的边。
合并连通块时别忘更新set和map,要启发式合并。
复杂度: \(O(n \log n + (m + q)\log^2 n)\)

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=100003;
struct edge{int to,next;}e[maxn<<2];
int head[maxn],cnte;
void add(int u,int v){e[++cnte].to=v,e[cnte].next=head[u],head[u]=cnte;}
int n,m,c,Q,f[maxn],sz[maxn];
vector<int> cc[maxn];
map<int,int> mp[maxn];
map<int,int>::iterator itmp;
set<int> st[maxn];
set<int>::iterator itst;
int find(int x){return x!=f[x]?f[x]=find(f[x]):f[x];}
void Union(int x,int y){int fx=find(x),fy=find(y);if(fx!=fy)f[fx]=fy;}
void merge(int x,int y){
    if(x==y)return;
    if(sz[x]>sz[y])swap(x,y);
    for(auto u:cc[x]){
        itst=st[u].find(x);
        if(itst!=st[u].end())st[u].erase(itst),st[u].insert(y);
        for(int i=head[u];i;i=e[i].next){
            int v=e[i].to;
            itst=st[v].find(x);
            if(itst!=st[v].end())st[v].erase(itst),st[v].insert(y);
        }
    }
    cc[y].insert(cc[y].end(),cc[x].begin(),cc[x].end());
    cc[x].clear();
    sz[y]+=sz[x];
    Union(x,y);
}
void addedge(int u,int v,int w){
    add(u,v),add(v,u);
    int fu=find(u),fv=find(v);
    st[v].insert(fu),st[u].insert(fv);
    itmp=mp[u].find(w);
    if(itmp!=mp[u].end()){
        int mpu=itmp->second;
        merge(fv,find(mpu));
    }
    mp[u][w]=v;
    itmp=mp[v].find(w);
    if(itmp!=mp[v].end()){
        int mpv=itmp->second;
        merge(fu,find(mpv));
    }
    mp[v][w]=u;
}
bool query(int x,int y){
    return st[y].count(find(x));
}
int main(){
    scanf("%d%d%d%d",&n,&m,&c,&Q);
    for(int i=1;i<=n;i++){
        cc[i].push_back(i);
        st[i].insert(i);
        f[i]=i;
        sz[i]=1;
    }
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        addedge(u,v,w);
    }
    char mo[2];
    int x,y,z;
    while(Q--){
        scanf("%s%d%d",mo,&x,&y);
        if(*mo=='+'){
            scanf("%d",&z);
            addedge(x,y,z);
        }
        else{
            puts(query(x,y)?"Yes":"No");
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/11041686.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值