2020 Multi-University Training Contest 3 1004,1005,1007,1009

168team0691
北京林业大学
411:47:00

1004:

如果是求和等于p,则直接用滑动窗口。

而这里是取模,我们依然可以类比滑动窗口,用map进行快速维护。

总体思想:贪心,前面能构造出尽量构造,给后面的数多一些选择空间

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
int a[M];
ll sm[M];
unordered_map<int,int>mp;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,p;
        scanf("%d%d",&n,&p);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),sm[i]=(sm[i-1]+a[i])%p;
        int ans=0,tp=0;
        mp[0]=1;
        for(int i=1;i<=n;i++)
        {
            if(a[i]%p==0)
            {
                mp.clear();
                ans++;
                tp=0;
                mp[0]=1;
            //    mp[(tp+a[i])%p]=i;
                continue;
            }
        //    cout<<i<<"  "<<tp+a[i]<<" "<<mp[(tp+a[i])%p]<<endl;
            if(mp[(tp+a[i])%p])
            {
                tp=0;
                mp.clear();
                ans++;
            }
            mp[(tp+a[i])%p]=1;
            tp+=a[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

1005:

显然是并查集,刚开始算总数量。

然后每连接一个关系就减去一些数量。

连接两个集合x,y时:

会少:

x中一个2,和y中一个2,以及补集中一个2的队伍个数。

x中一个1,和y中一个2,以及补集中一个2的队伍个数。

x中一个2,和y中一个1,以及补集中一个2的队伍个数。

x中一个2,和y中一个2,以及补集中一个1的队伍个数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
const int mod =1e9+7;
int fa[M];
ll sz1[M],sz2[M];
int gt(int x)
{
    if(x==fa[x])return x;
    return fa[x]=gt(fa[x]);
}
int a[M];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        ll nm1=0,nm2=0;
        for(int i=1;i<=n;i++)fa[i]=i,sz1[i]=sz2[i]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==1)nm1++,sz1[i]=1;
            else nm2++,sz2[i]=1;
        }
        ll ans=nm2*(nm2-1)/2%mod*nm1%mod;
        if(nm2>2)ans+=nm2*(nm2-1)*(nm2-2)/6%mod;
        ans%=mod;
        printf("%lld\n",ans);
        for(int tt=1;tt<n;tt++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            int gx=gt(x),gy=gt(y);
            if(gx!=gy)
            {
                ll tp=0,z=0;//(1,2,'2')(2,1,'2')(2,2,'1')(2,2,'2')
                
                z=nm2-sz2[gx]-sz2[gy];
                if(z>0)tp+=sz1[gx]*sz2[gy]*z%mod+sz1[gy]*sz2[gx]*z%mod+sz2[gy]*sz2[gx]*z%mod;tp%=mod;
                
                z=nm1-sz1[gx]-sz1[gy];
                if(z>0)tp+=sz2[gx]*sz2[gy]*z%mod;tp%=mod;
                
                ans=((ans-tp)%mod+mod)%mod;
                
                fa[gy]=gx;
                sz1[gx]+=sz1[gy];
                sz2[gx]+=sz2[gy];
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}

1007:

暴力做法:

每次都从最短路上删一条边,删五次。

我跑了个随机数据发现:平均最短路边数为4,且只有一条。

然后直接模拟上述过程即可。

复杂度为:

O(w^5 *2500*10)

w为每次最短路的期望边数。

刚开始我是按w为10跑的,但发现随机数据甚至小于10,所以直接写即可。

一些删边/村边/打印边  小技巧都在代码里了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const int N = 50+7; 
const int M = 4*50*50+7;

int head[N],cnt=1;
int idu[M],idv[M];
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){
    ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;
    idu[cnt]=x;
    idv[cnt]=y;
    //cout<<" ====="   <<x<<"  "<<y<<"  "<<ee[cnt].nxt<<"  "<<cnt<<endl;
}

int n,k;
int d[N],vs[N],del[M];
int a[N][N],b[N][N];
void dij()
{
    memset(d,0x3f,sizeof(d));
    memset(vs,0,sizeof(vs));
    d[1]=0;
    for(int i=1;i<n;i++)
    {
        int x=0;
        for(int j=1;j<=n;j++)
            if(!vs[j]&&(x==0||d[j]<d[x]))x=j;
        vs[x]=1;
        for(int y=1;y<=n;y++)
            d[y]=min(d[y],d[x]+a[x][y]);
    }
}
int mx=0;
vector<int>v[7];
int mp[M],vv[N];
void gt(int x,int ty)
{
    vv[x]=1;
//    cout<<x<<"  -   "<<v[ty].size()<<"  "<<ty<<"  "<<"  -= "<<endl;
    for(int i=head[x];i;i=ee[i].nxt)
    {
        int y=ee[i].to,w=ee[i].w;
        if(del[i]||vv[y])continue;
     //   cout<<x<<"  ------------   "<<y<<"   "<<i<<"   "<<ee[i].nxt<<endl;
        if(d[x]==d[y]+w)
        {
            if(!mp[i])v[ty].pb(i),gt(y,ty);
            mp[i]=1;
        }
    }
}
void dfs(int nm)
{
    if(nm==k+1)
    {
        dij();mx=max(mx,d[n]);
        return ;
    }
   // cout<<"okok"<<endl;
    dij();
    v[nm].clear(); 
    memset(vv,0,sizeof(vv));
    memset(mp,0,sizeof(mp));
   // cout<<" opk  "<<endl;
    gt(n,nm);
   // cout<<v[nm].size()<<"  -- --   "<<endl;
    for(auto x:v[nm])
    {
        del[x]=del[x^1]=1;
        int u=idu[x],v=idv[x];
        int tp=a[u][v];
        a[u][v]=a[v][u]=1e9+7;
        dfs(nm+1);
        del[x]=del[x^1]=0;
        a[u][v]=a[v][u]=tp;
    }
}
int main()
{
//    freopen("1.in","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        cnt=1,memset(head,0,sizeof(head)),mx=0;
        for(int i=1;i<=5;i++)v[i].clear();
        int u,v,w;
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n*(n-1)/2;i++)
        {
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w),add(v,u,w);
            a[u][v]=a[v][u]=w;
        }
        dfs(1);
        printf("%d\n",mx);
    }
    return 0;
}

1009

括号序列经典问题:

只要一个括号序列任意前缀和大于0,且最后所有和等于0.(   注意'('=1.')'=-1  )

这一题要求最短,且字典序最小。

就是说,在满足它是个括号序列的基本条件下,(尽量在左边,)尽量在右边。

所以我们直接第一遍加(,满足任意前缀大于0,尽量在前面加左括号,用min(i+1,n)来查找后面是否都满足条件。

每次更新前缀和。由于更新累加,可以直接记录差值,O1的进行查询。

最后从后往前加')',满足所有和等于0.

最后再判断一次是否满足括号序列即可。

上述做法正确性显然:

1:左括号加的尽量少,且尽量在左边。

2:右括号加的尽量少,且尽量在右边。

如果最后依然不满足,则一定不能构造出正解

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
/*
int head[M],cnt=1;
void init(){cnt=1,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y,int w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
*/
char s[M];
int a[M];
int mn[M];
int main()
{
      int T;
      scanf("%d",&T);
      while(T--)
      {
          scanf("%s",s+1);
        bool flag=true;
        int n=strlen(s+1);
        for(int i=1;i<=n;i++)
        {
            a[i]=a[i-1];
            if(s[i]=='(')a[i]++;
            else if(s[i]==')')a[i]--;
        }
        mn[n+1]=n+1;
        for(int i=n;i>=1;i--)
            mn[i]=min(mn[i+1],a[i]);
        int det=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='*')
            {
                if(mn[i+1]+det<0)
                {
                    s[i]='(';
                    det++;
                }
            }
            if(a[i]+det<0)flag=false;
        }
        if(!flag)
        {
            puts("No solution!");
            continue;
        }
        int nm=a[n]+det;
        for(int i=n;i>=1;i--)
        {
            if(s[i]=='*'&&nm>0)
            {
                nm--;
                s[i]=')';
            }
        }
        int tp=0;
        for(int i=1;i<=n;i++)
        {
            if(s[i]=='(')tp++;
            else if(s[i]==')')tp--;
            if(tp<0)flag=false;
        }
        if(tp!=0)flag=false;
        if(!flag)puts("No solution!");
        else 
        {
            for(int i=1;i<=n;i++)if(s[i]!='*')printf("%c",s[i]);
            puts("");
        }
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值