2022/2/4

142 篇文章 0 订阅
92 篇文章 0 订阅

c Strange Test

也就3种情况:1.a直接加到b,2.a加到某个数之后或到b,3.b加到某个数a或到b;所以这样来枚举就可以了

(2条消息) 768、C. Strange Test(与运算) 769、C - And Matching(或运算)_linglingnana的博客-CSDN博客

#include<bits/stdc++.h>
#define ll long long
typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}
/*void init_set(){
    for(int i=1;i<=n;i++) s[i]=i,hei[i]=0;
}
int find_set(int x){
    if(x!=s[x]) s[x]=find_set(s[x]);
    return s[x];
}*/
/*void union_set(int x,int y){
    x=find_set(x);
    y=find_set(y);
    if(hei[x]==hei[y]){
        hei[x]=hei[x]+1;
        s[y]=x;
    }
    else{
        if(hei[x]<hei[y]) s[x]=y;
        else s[y]=x;
    }
}*/
ll t,a,b;
int main(){
    //freopen("in.txt","r",stdin);
    cin>>t;
    while(t--){
        cin>>a>>b;
        if((a|b)==b){cout<<1<<endl;continue;}
        ll ans=b-a;
        for(int i=1;i+1<=ans;i++){
            if(((a+i)|b)==b||((b+i)|a)==b+i) ans=i+1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

CF1594D The Number of Imposters 并查集

我们考虑每一条信息可以告诉我们什么:

  • 假设 A 说 B 是老实人,那么:
    • 如果 A 是老实人,则 B 也是老实人。
    • 如果 A 是骗子,由于 B 不是老实人,所以 B 必然为骗子。
  • 如果 A 说 B 是骗子:
    • A 说实话,B 是骗子。
    • A 说谎,B 是老实人。

同样的,如果 A 说 B 是老实人,而 B 确实是老实人,则 A 说的是实话,也是老实人。

用并查集来维护这个规则,令(1,n)是诚实的人,(n+1,2n)是骗子

做统计的时候考虑每个集合的代表是老实人/骗子的情况下,哪种老实人数量更多,累加起来就是答案。

CF1594D 题解 - PragmaGCC 的博客 - 洛谷博客 (luogu.com.cn)

#include<bits/stdc++.h>
#define ll long long
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}
/*void init_set(){
    for(int i=1;i<=n;i++) s[i]=i,hei[i]=0;
}
int find_set(int x){
    if(x!=s[x]) s[x]=find_set(s[x]);
    return s[x];
}*/
/*void union_set(int x,int y){
    x=find_set(x);
    y=find_set(y);
    if(hei[x]==hei[y]){
        hei[x]=hei[x]+1;
        s[y]=x;
    }
    else{
        if(hei[x]<hei[y]) s[x]=y;
        else s[y]=x;
    }
}*/
char s[200005];
int t,n,m,fa[400005],siz[400005];
int findd(int x){
    if(x==fa[x]) return x;
    fa[x]=findd(fa[x]);
    siz[fa[x]]+=siz[x];
    siz[x]=0;
    return fa[x];
}
void mergee(int x,int y){
    int fx=findd(x),fy=findd(y);
    if(fx==fy) return;
    siz[fx]+=siz[fy];siz[fy]=0;
    fa[fy]=fx;
}
int main(){
    //freopen("in.txt","r",stdin);
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n<<1;i++) fa[i]=i,siz[i]=i<=n;
        for(int i=1,x,y;i<=m;i++){
            cin>>x>>y>>s;
            if(s[0]=='c') mergee(x,y),mergee(x+n,y+n);
            else mergee(x,y+n),mergee(y,x+n);
        }
        int ans=0;
        for(int i=1;i<=n;i++){
            if(findd(i)==findd(i+n)){ans=-1;break;}// 一个人既诚实又说谎,出现矛盾
            ans+=max(siz[findd(i)],siz[findd(i+n)]);
            siz[findd(i)]=siz[findd(i+n)]=0;
        }
        cout<<ans<<endl;
    }
    return 0;
}

p6691

和上一个题类似,但比上一题难,话说难为啥这题黄上一个题绿。。。

但是这里有两种对应关系——真话和假话,真话边连接的两点是同对同错的,否则无解。假话边则相反。所以我们的边要带一个边权,之后通过遍历将边的信息转移到点上(哪些点同对错,哪些点对错性相反)边权为 0 表示为真话边,边权为 1 表示为假话边。

为什么要这样设置呢? 按题目中 1 对应真不好吗?

这里的信息要进行传递,A说B是假,B说C是假,那么A和C就是同对同错的了。所以我想到一个神奇的位运算——异或(在c++ 中用 ^ 表示)。

0 ^ 0 = 0 真边 + 真边 = 真边

1 ^ 0 = 1 假边 + 真边 = 假边

1 ^ 1 = 0 假边 + 假边 = 真边

上面源自题解 P6691 【选择题】 - llzzxx712 的博客 - 洛谷博客 (luogu.org)

然后这个规则对应的代码中的r数组,来处理结点与上一个结点之间的关系,然后代码里也放注释了

#include<bits/stdc++.h>
#define ll long long
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}
/*void init_set(){
    for(int i=1;i<=n;i++) s[i]=i,hei[i]=0;
}
int find_set(int x){
    if(x!=s[x]) s[x]=find_set(s[x]);
    return s[x];
}*/
/*void union_set(int x,int y){
    x=find_set(x);
    y=find_set(y);
    if(hei[x]==hei[y]){
        hei[x]=hei[x]+1;
        s[y]=x;
    }
    else{
        if(hei[x]<hei[y]) s[x]=y;
        else s[y]=x;
    }
}*/
int f[2000006],r[2000006],num[2000005][2],n;
int ans1,ans2,x,op,tot=1;
int findd(int x){
    if(f[x]==x) return f[x];
    int t=f[x];
    f[x]=findd(f[x]);
    r[x]=(r[t]+r[x])%2;//相当于在做异或
    return f[x];
}
void mergee(int i,int x,int op){
    int tmp=findd(i);
    f[tmp]=findd(x);
    r[tmp]=(r[i]+r[x]+op+1)%2;//加1是为了对应思路中的0为真,1为假
    return;
}
int main(){
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=n;i++){
        cin>>x>>op;
        if(findd(i)!=findd(x))
            mergee(i,x,op);
        else{
            if((r[i]+r[x])%2!=(op+1)%2){//异或值不相同,第一条边共和第三条边权值不等,说明冲突
                cout<<"No answer"<<endl;
                return 0;
            }
        }
    }
    for(int i=1;i<=n;i++)
        num[findd(i)][r[i]]++;//统计答案的个数
    for(int i=1;i<=n;i++){
        if(findd(i)==i){
            tot=tot*2%998244353;
            ans1+=max(num[i][1],num[i][0]);//累计加最大的
            ans2+=min(num[i][1],num[i][0]);//累计加最小的
        }
    }
    cout<<tot<<endl<<ans1<<endl<<ans2<<endl;
    return 0;
}

p3374 树状数组 单点修改区间查询模板

#include<bits/stdc++.h>
#define ll long long
#define lowbit(a) ((a)&(-a))
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}
int n,m,t[500005];
void add(int x,int y){//树状数组单点修改
    for(int i=x;i<=n;i+=lowbit(i))
        t[i]+=y;
}
int getsum(int x){//树状数组区间查询
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        ans+=t[i];
    return ans;
}
int main(){
    //freopen("in.txt","r",stdin);
    cin>>n>>m;
    int a;
    for(int i=1;i<=n;i++){
        cin>>a;add(i,a);
    }
    int x,y;
    while(m--){
        cin>>a>>x>>y;
        if(a==1)
            add(x,y);
        else cout<<getsum(y)-getsum(x-1)<<endl;
    }
    return 0;
}

 p4939 树状数组 区间修改 单点查询模板

#include<bits/stdc++.h>
#define ll long long
#define lowbit(a) ((a)&(-a))
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=1000000007;
const double eps=1e-11;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(int a,int b){return a>b;}
ll n,m,x,y,op,a[10000007],t[10000007];
void add(ll x,ll y){
    for(int i=x;i<=n;i+=lowbit(i))
        t[i]+=y;
}
void rangeadd(ll l,ll r,ll x){
    add(l,x);add(r+1,-x);
}
ll ask(ll x){
    ll res=0;
    for(int i=x;i;i-=lowbit(i))
        res+=t[i];
    return res;
}
int main(){
   // freopen("in.txt","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++) t[i]=0;
    while(m--){
        op=read();
        if(op){x=read();cout<<ask(x)<<endl;}
        else{x=read();y=read();rangeadd(x,y,1);}
    }
    return 0;
}

 p3372线段树模板

#include<bits/stdc++.h>
#define ll long long
#define lowbit(a) ((a)&(-a))
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}
ll a[100005],n,m,x,y,k,q;
struct segtree{
    ll val,lazy;
}tree[100005<<2];
void pushup(ll rt){tree[rt].val=tree[rt<<1].val+tree[rt<<1|1].val;}
void build(ll l,ll r,ll rt){
    tree[rt].lazy=0;
    if(l==r){
        tree[rt].val=a[l];
        return;
    }
    ll m=l+r>>1;
    build(l,m,rt*2);
    build(m+1,r,rt*2+1);
    pushup(rt);
}
//L,R为目标区间,l,r为当前区间,C是要加的值,rt是当前数组下标
void pushdown(ll rt,ll ln,ll rn){
    if(tree[rt].lazy){//下推标记
        tree[rt<<1].lazy+=tree[rt].lazy;
        tree[rt<<1|1].lazy+=tree[rt].lazy;
        tree[rt<<1].val+=tree[rt].lazy*ln;
        tree[rt<<1|1].val+=tree[rt].lazy*rn;
        tree[rt].lazy=0;//置为0,以免以后访问时造成混乱
    }
}
void update(ll L,ll R,ll C,ll l,ll r,ll rt){
    if(L<=l&&r<=R){
        tree[rt].val+=C*(r-l+1);
        tree[rt].lazy+=C;
        return;
    }
    ll m=l+r>>1;
    pushdown(rt,m-l+1,r-m);//m-l+1,r-m都是区间大小,下推标记
    if(L<=m) update(L,R,C,l,m,rt<<1);
    if(R>m) update(L,R,C,m+1,r,rt<<1|1);
    pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt){
    if(L<=l&&r<=R) return tree[rt].val;
    if(L>r||R<l) return 0;
    ll m=l+r>>1;
    pushdown(rt,m-l+1,r-m);//下推以后才能查询子节点,不然子节点是未改变的状态
    return query(L,R,l,m,rt<<1)+query(L,R,m+1,r,rt<<1|1);
}
int main(){
    //freopen("in.txt","r",stdin);
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,n,1);
    while(m--){
        cin>>q;
        if(q==1){
            cin>>x>>y>>k;
            update(x,y,k,1,n,1);
        }
        else{
            cin>>x>>y;
            ll ans=query(x,y,1,n,1);
            cout<<ans<<endl;
        }
    }
    return 0;
}

p3373 线段树区间修改加法与乘法模板

#include<bits/stdc++.h>
#define ll long long
#define lowbit(a) ((a)&(-a))
//typedef __int128 LL;
using namespace std;
const int inf=0x3f3f3f3f;
const int mod=92084931;
ll read() {//快读
    ll x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
void write(int x) {//快写
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
ll qpow(ll a,ll b){//快速幂
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll getinv(ll a,ll mod){return qpow(a,mod-2);}//费马小定理求逆元
bool cmp1(ll a,ll b){return a>b;}


ll a[100005],n,m,p,x,y,k,q;
struct segtree{
    ll val,lazy[3];
}tree[100005<<2];
void pushup(ll rt){tree[rt].val=(tree[rt<<1].val+tree[rt<<1|1].val)%p;}
void build(ll l,ll r,ll rt){
    tree[rt].lazy[1]=0;
    tree[rt].lazy[2]=1;
    if(l==r){
        tree[rt].val=a[l];
        return;
    }
    ll m=l+r>>1;
    build(l,m,rt*2);
    build(m+1,r,rt*2+1);
    pushup(rt);
}
//L,R为目标区间,l,r为当前区间,C是要加的值,rt是当前数组下标
void pushdown(ll rt,ll ln,ll rn){
        //下推到子节点
        tree[rt<<1].lazy[2]=(tree[rt<<1].lazy[2]*tree[rt].lazy[2])%p;
        tree[rt<<1|1].lazy[2]=(tree[rt<<1|1].lazy[2]*tree[rt].lazy[2])%p;
        tree[rt<<1].lazy[1]=(tree[rt<<1].lazy[1]*tree[rt].lazy[2]+tree[rt].lazy[1])%p;
        tree[rt<<1|1].lazy[1]=(tree[rt<<1|1].lazy[1]*tree[rt].lazy[2]+tree[rt].lazy[1])%p;
        //儿子的值=此刻儿子的值*爸爸的乘法lazytag+儿子的区间长度*爸爸的加法lazytag
        tree[rt<<1].val=(tree[rt<<1].val*tree[rt].lazy[2]+tree[rt].lazy[1]*ln)%p;
        tree[rt<<1|1].val=(tree[rt<<1|1].val*tree[rt].lazy[2]+tree[rt].lazy[1]*rn)%p;
        //把父节点的值初始化
        tree[rt].lazy[1]=0;
        tree[rt].lazy[2]=1;
}
void update1(ll L,ll R,ll C,ll l,ll r,ll rt){//乘法更新
    ll m=l+r>>1;
    if(R<l||L>r) return;
    if(L<=l&&r<=R){
        tree[rt].val=(tree[rt].val*C)%p;
        tree[rt].lazy[1]=(tree[rt].lazy[1]*C)%p;
        tree[rt].lazy[2]=(tree[rt].lazy[2]*C)%p;
        return;
    }
    pushdown(rt,m-l+1,r-m);
    if(L<=m) update1(L,R,C,l,m,rt<<1);
    if(R>m) update1(L,R,C,m+1,r,rt<<1|1);
    pushup(rt);
}
void update2(ll L,ll R,ll C,ll l,ll r,ll rt){//加法更新
    ll m=l+r>>1;
    if(R<l||L>r) return;
    if(L<=l&&r<=R){
        tree[rt].val=(tree[rt].val+C*(r-l+1))%p;
        tree[rt].lazy[1]=(tree[rt].lazy[1]+C)%p;
        return;
    }
    pushdown(rt,m-l+1,r-m);
    if(L<=m) update2(L,R,C,l,m,rt<<1);
    if(R>m) update2(L,R,C,m+1,r,rt<<1|1);
    pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt){
    if(L<=l&&r<=R) return tree[rt].val;
    if(L>r||R<l) return 0;
    ll m=l+r>>1;
    pushdown(rt,m-l+1,r-m);//下推以后才能查询子节点,不然子节点是未改变的状态
    return (query(L,R,l,m,rt<<1)+query(L,R,m+1,r,rt<<1|1))%p;
}
int main(){
   // freopen("in.txt","r",stdin);
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++) cin>>a[i];
    build(1,n,1);
    while(m--){
        cin>>q;
        if(q==1){
            cin>>x>>y>>k;
            update1(x,y,k,1,n,1);
        }
        else if(q==2){
            cin>>x>>y>>k;
            update2(x,y,k,1,n,1);
        }
        else{
            cin>>x>>y;
            ll ans=query(x,y,1,n,1);
            cout<<ans%p<<endl;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

killer_queen4804

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值