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

168 team0691
北京林业大学
4 11:47:00

1004:

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

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

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

#include 
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_mapmp;
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<

1005:

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

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

连接两个集合x,y时:

会少:

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

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

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

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

#include 
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;tt0)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 
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<<" ====="   <v[7];
int mp[M],vv[N];
void gt(int x,int ty)
{
    vv[x]=1;
//    cout<

1009

括号序列经典问题:

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

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

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

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

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

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

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

上述做法正确性显然:

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

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

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

#include 
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;
}

 

你可能感兴趣的:(2020多校hdu)