叫高二上一调?

考的烂得不行,还是应该多敲暴力搞分啊。。。

A. 电压机制

首先容易发现实际上是找去掉一条边后,剩下的是一个二分图的边数

然后发现这条边一定不在偶环上,一定在所有奇环上(或者根本没有奇环)

如果\(DFS\)显然不对,因为你没法打\(vis\),所以考场上想到这就跳了。。

实际上,这种跟环有关的,都可以借鉴一下\(tarjan\)的部分思想

比如我们搞出了\(DFS\)树,然后考虑非树边,由于是\(DFS\)树,而且是无向图,所以成环的边一定是返祖边,然后我们就可以通过树上查分来记录每条边在多少个奇环上

然后再考虑一下非树边,这里粘一下题解
image
也就是说当且仅当有一个奇环时,这个非树边才有贡献,特判即可

code
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f;
const int maxn=100005;
const int maxm=500005;
int n,m,dep[maxn],tim,tot=1,head[maxn],cf[maxn],odd;
struct edge{int net,to;}e[maxm];
void add(int u,int v){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
}
bool vis[maxm];
void dfs(int u){
    for(int i=head[u];i;i=e[i].net){
        if(vis[i])continue;
        vis[i]=vis[i^1]=1;
        int v=e[i].to;
        if(dep[v]){
            if((dep[u]-dep[v])%2){
                ++cf[v];--cf[u];
            }else{
                --cf[v];++cf[u];++odd;
            }
        }
        else{dep[v]=dep[u]+1;dfs(v);}
    }
}
void dfs_2(int x){
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;
        if(vis[v])continue;
        vis[v]=1;
        dfs_2(v);
        cf[x]+=cf[v];
    }
}

int main(){
    // freopen("a.in","r",stdin);
    // freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;++i){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dep[1]=1;
    dfs(1);
    for(int i=1;i<=n;++i)printf("%d ",cf[i]);puts("");
    for(int i=1;i<=n;++i)vis[i]=0;
    vis[1]=1;dfs_2(1);
    int ans=0;
    for(int i=2;i<=n;++i)ans+=cf[i]==odd?1:0;
    if(odd==1&&ans!=0)++ans;
    printf("%d\n",ans);
    return 0;
}

B. 括号密码

显然有重叠的区间可以拆开,因为他们的交集必然合法

包含的区间,小区间必须合法(废话),大区间扔掉小区间后是个合法区间即可

区间长度为奇数无解

先考虑如何拆区间

考场假做法:对于第\(i\)个要求的区间,每个位置加上\(i\),然后根据每个位置的数字进行判断,假的

\(cd\)优化:把加\(i\)搞成进位取模,相当于一个\(hash\),可能能过

\(STL\)乱搞大法:原来\(STL\)还能这么用。。,对每个位置开一个\(vector\),每个要求往对应位置插\(i\),然后用map映射vector

对于包含区间,将区间按长度排序处理,打个\(vis\)即可

然后考虑处理一个区间,用栈模拟出来,统计\(cz\)\(cy\)表示有多少非法左右括号

其实可以发现,最后非法的串一定是\()))....(((\),我们需要尽少的次数使其合法,发现一次操作可以消去一对或者两对非法括号

我们尽可能消去两对,发现如果只剩下一个左/右括号时,我们必须操作一次消去这组非法括号

那么实际上我们可以直接写成\(ans+=(min(cz,cy)+1)/2\)

然后这时,这个区间至多有一种括号,而且一定为偶数个,我们先统计下来最后统一处理

处理时,考虑两个区间的左右括号,一次操作一定能消去两对,对于剩下的一种括号,我们必须从没有要求的区间里搞来不同的括号,我们只需要剩余括号数量/2个括号,也就是这么多次操作即可,记得判一下括号够不够用。

code
#include<map>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f;
const int maxn=20000005;
int vis[maxn],n,len,rm[maxn],cnt;
char s[maxn];
struct op{int l,r,len;}o[maxn];
bool cmp(op x,op y){return x.len<y.len;}
int zuo,you,ans,rem[maxn];
char sta[maxn];int top;
vector<int>v[2005];
map<vector<int>,int>mp;
int work(){
    for(int i=1;i<=n;++i)if((o[i].r-o[i].l)%2==0)return -1;
    for(int i=1;i<=n;++i)
        for(int j=o[i].l;j<=o[i].r;++j)v[j].push_back(i);
    for(int i=0;i<len;++i){
        if(v[i].size()==0)continue;
        if(mp[v[i]]==0){mp[v[i]]=++cnt;o[cnt].l=i;}
        o[mp[v[i]]].r=i;
    }
    for(int i=1;i<=cnt;++i){
        o[i].len=o[i].r-o[i].l;
        if(o[i].len%2==0)return -1;
    }
    sort(o+1,o+cnt+1,cmp);
    
    for(int i=1;i<=cnt;++i){
        top=0;
        for(int j=o[i].l;j<=o[i].r;++j){
            if(rem[j]){j=rem[j];continue;}
            if(s[j]==')'&&top&&sta[top]=='(')--top;
            else sta[++top]=s[j];
        }
        rem[o[i].l]=o[i].r;
        int cz=0,cy=0;
        for(int j=1;j<=top;++j)if(sta[j]=='(')++cz;else ++cy;
        if(cz>=cy){ans+=(cy+1)/2;you+=cz-cy;}
        else{ans+=(cz+1)/2;zuo+=cy-cz;}
    }
    int cz=0,cy=0;
    for(int i=0;i<len;++i){
        if(rem[i]){i=rem[i];continue;}
        if(s[i]=='(')++cz;
        else ++cy;
    }
    zuo/=2;you/=2;
    if(zuo>=you){
        ans+=you;
        zuo-=you;
        if(zuo>cz)return -1;
        else return ans+zuo;
    }else{
        ans+=zuo;
        you-=zuo;
        if(you>cy)return -1;
        else return ans+you;
    }
}
int main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%s",s);len=strlen(s);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&o[i].l);
    for(int i=1;i<=n;++i)scanf("%d",&o[i].r);
    printf("%d\n",work());
    return 0;
}

C. 内积

结论题,排序不等式,排序乘起来即可。

code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f;
const int maxn=1000055;
ll a[maxn],b[maxn],ans;
int main(){
    freopen("nj.in","r",stdin);
    freopen("nj.out","w",stdout);
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    for(int i=1;i<=n;++i)scanf("%lld",&b[i]);
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    ll ans=0;
    for(int i=1;i<=n;++i)ans+=a[i]*b[i];
    printf("%lld\n",ans);
    return 0;
}

D. 排列

考场搞了三棵值域树状数组,还写挂了,后来发现前缀和一下就行了。。。

不过最后用的还是\(cdsidi\)大佬的解法

先考虑\(a[1],a[2]\)之间和\(a[3],a[4]\)之间的关系,(就是枚举所有二元组)

然后枚举\(a[1],a[2]\),在二维树状数组里查询即可,具体做法可以参考代码,赶进度,不详细写了。

code
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int inf=0x3f3f3f;
const int maxn=5005;
int n,a[11],b[maxn];
int q[maxn][maxn],h[maxn][maxn];
struct tree{
    int t[maxn][maxn];
    int lowbit(int x){return x&-x;}
    void add(int x,int y,int d){
        if(d==0)return;
        for(int i=x;i<=n;i+=lowbit(i))
          for(int j=y;j<=n;j+=lowbit(j))
            t[i][j]+=d;
    }
    int get(int x,int y){
        int ans=0;
        if(x<=0||y<=0)return 0;
        for(int i=x;i;i-=lowbit(i))
          for(int j=y;j;j-=lowbit(j))
            ans+=t[i][j];
        return ans;
    }
    int query(int x1,int x2,int y1,int y2){
        return get(x2,y2)+get(x1-1,y1-1)-get(x1-1,y2)-get(x2,y1-1);
    }
}T;


void pushpos(int i){
    if(a[3]<a[4]){for(int j=b[i]+1;j<=n;++j)if(h[b[i]][j])T.add(b[i],j,1);}
    else {for(int j=1;j<b[i];++j)if(h[b[i]][j])T.add(j,b[i],1);}
}

ll work(){
    for(int i=1;i<=n;++i)
      for(int j=i+1;j<=n;++j)
        if((a[2]-a[1])*(b[j]-b[i])>0)
          q[b[i]][b[j]]=1;
    for(int i=1;i<=n;++i)
      for(int j=i+1;j<=n;++j)
        if((a[4]-a[3])*(b[j]-b[i])>0)
          h[b[i]][b[j]]=1;
    
    int m1=min(a[1],a[2]),m2=max(a[1],a[2]);
    ll ans=0;
    for(int i=n-2;i>=2;--i){
        pushpos(i+1);
       for(int j=1;j<=n;++j){
           if(q[j][b[i]]==0)continue;
           int mi=min(b[i],j),mx=max(b[i],j);
           if(m1==1&&m2==2){ans+=T.query(mx+1,n,1,n);continue;}
           if(m1==1&&m2==3){ans+=T.query(mi+1,mx-1,mx+1,n);continue;}
           if(m1==1&&m2==4){ans+=T.query(mi+1,mx-1,mi+1,mx-1);continue;}
           if(m1==2&&m2==3){ans+=T.query(1,mi-1,mx+1,n);continue;}
           if(m1==2&&m2==4){ans+=T.query(1,mi-1,mi+1,mx-1);continue;}
           if(m1==3&&m2==4){ans+=T.query(1,mi-1,1,mi-1);continue;}
       }
    }
    return ans;
}

int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=4;++i)cin>>a[i];
    for(int i=1;i<=n;++i)cin>>b[i];
    cout<<work()<<'\n';
    return 0;
}
posted @ 2022-06-27 20:30  Chen_jr  阅读(44)  评论(0编辑  收藏  举报