7.28 18级杭电多校第三场

比赛过程

  感觉这场是手速场,三道题出的快直接100多名了,但是我今天又犯了莽的错误,罚时太多了,锅很大。。。

题解

1004

题意

  翻译过来就是给你一个序列,让你给序列分组,每一组必须是连续的元素,问你怎么分才能得到组元素之和是 p p p 的倍数的最多组数。

解法

  贪心思想,如果某连续 x x x 个元素的和满足条件,直接让这 x x x 个元素为一组就可以,因为就算包含了后边的元素,他们的贡献还是 1 1 1
  比赛的时候想的太复杂了,如果部分前缀和是 s u m sum sum 其实你只要拿 m a p map map s u m m o d    p sum\mod p summodp ,然后当你发现遍历到某一个数 s u m ′ m o d    p sum'\mod p summodp 在map里面存在就说明可以通过减去map里面那个前缀来满足条件,之后更新答案, m a p map map 清空,因为你第二次遇到那个数的时候,此时的部分前缀和肯定要比第一次遇到那个数的时候大,所以肯定是可以减去的。

代码
#include<bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'
#define FOR(a,b,c) for(int a=b;a<=c;a++)
#define RFOR(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
typedef pair<int,int> P;
const ll mod = 1e9+7;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int a[maxn];
int main(){
    IO;
    int t;
    cin >> t;
    while(t--){
        int n, p;
        cin >> n >> p;
        FOR(i,1,n){
            cin >> a[i];
        }
        map<int, int> mp;
        mp[0] = 1;
        int tmp = 0, ans = 0;
        FOR(i,1,n){
            tmp += a[i];
            tmp %= p;
            if(mp[tmp]){
                tmp = 0;
                ans++;
                mp.clear();
                mp[0] = 1;
            }
            else mp[tmp] = 1;
        }
        cout << ans << endl;
    }
    return 0;
}

1005

题意

   假如现在有两个技能,分别为 a , b a,b a,b,一个人有且只有其中的一个,现在给你一堆带技能的人,最开始所有人都不认识其他任何一个人,现在要选三个人组队,组队的要求是:1、至少有两个人的技能是 b b b,2、这三人必须都不认识。
   现在有 n − 1 n-1 n1 个操作,每个操作会把两个不认识的人介绍认识,注意人与人之间的认识有传递性。求刚开始和每次操作后能够组成一队的三人组合数量。

解法

   并查集,每次连接认识的俩人在的连通块,然后减去原来两组中可以组成一队的三人组合数量,输出即可。

代码
ll Pow(ll a, ll b, ll mode)
{
  ll sum = 1;
  a = a % mode;
  while (b > 0)
  {
    if (b % 2 == 1) //判断是否是奇数,是奇数的话将多出来的数事先乘如sum
      sum = (sum * a) % mode;
    b >>= 1;
    a = (a * a) % mode; // 不断的两两合并再取模,减小a和b的规模
  }
  return sum;
}
ll inv(ll x)
{
    return Pow(x,mode-2,mode);
}
ll pre[MAXN];
void init(int n){
    for(int i=1;i<=n;i++)
        pre[i] = i;
}
int find(int x){
    if(pre[x]==x) return x;
    return pre[x]=find(pre[x]);
}
void join(int x,int y){
    int fx = find(x), fy = find(y);
    if(fx!=fy) {
        pre[fy]=fx;
    }
}
ll ji[MAXN],cnt1[MAXN],cnt2[MAXN];
ll C(ll x,ll y)
{
    ll tem=(ji[y]*inv(ji[y-x])%mode)*inv(ji[x])%mode;
    return tem;
}
//----------------------------------------------------------------

int main()
{
    IO;
    ll t;
    cin>>t;
    ji[0]=1;
    re(i,1,1e5+5)
    {
        ji[i]=i*ji[i-1]%mode;
    }
    while(t--)
    {
        int n;
        cin>>n;
        ms(cnt1,0);
        ms(cnt1,0);
        init(n+5);
        ll c1=0,c2=0;
        re(i,1,n){int a;cin>>a;if(a==1)c1++,cnt1[i]=1,cnt2[i]=0;else c2++,cnt1[i]=0,cnt2[i]=1;}
        ll ans=0;
        if(c2>=2&&c1>=1)ans=(ans+C(2,c2)*C(1,c1)%mode)%mode;
        if(c2>=3)ans=(ans+C(3,c2)%mode)%mode;
        cout<<ans<<endl;
        re(i,1,n-1)
        {
            int u,v;
            cin>>u>>v;
            if(n-i<3){cout<<0<<endl;continue;}
            int fu=find(u);
            int fv=find(v);
            ll rc1=c1-cnt1[fu]-cnt1[fv];
            ll rc2=c2-cnt2[fu]-cnt2[fv];
            ll tem=0;
            if(cnt2[fu]>=1&&cnt1[fv]>=1)tem+=cnt2[fu]*cnt1[fv]%mode*rc2%mode;
            if(cnt2[fv]>=1&&cnt1[fu]>=1)tem+=cnt2[fv]*cnt1[fu]%mode*rc2%mode;
            if(cnt2[fv]>=1&&cnt2[fu]>=1)tem+=cnt2[fv]*cnt2[fu]%mode*((rc1+rc2)%mode)%mode;
            tem%=mode;
            ans=(ans-tem+mode)%mode;
            cout<<ans<<endl;
            cnt2[fu]+=cnt2[fv];
            cnt1[fu]+=cnt1[fv];
            join(u,v);
        }
    }

}

1009

题意

  给你一个包含 三种元素的字符串,比如 ( ( ∗ ∗ ∗ ) ( ) ( ( ∗ ∗ ((***)()((** (()()((,其中 ∗ * 可以变成 ) ( )( )( 中任意一个或者直接删掉,问你能得到的最短的括号平衡串的长度,长度相同的话选择字典序最小的。

解法

  其实只要正序逆序各处理一边就可以了,正序处理是让所有的 ) ) ) 满足要求,逆向是让所有的 ( ( ( 满足要求。

代码
#include<bits/stdc++.h>
#define ll long long
#define IO ios::sync_with_stdio(false), cin.tie(0)
#define endl '\n'
#define FOR(a,b,c) for(int a=b;a<=c;a++)
#define RFOR(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
typedef pair<int,int> P;
const ll mod = 1e9+7;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
int main(){
   IO;
    int t;
    cin >> t;
    while(t--){
        string s;
        cin >> s;
        int n = s.size();
        int f = 0;
        deque<int> q;
        FOR(i,0,n-1){
            if(s[i]=='(') f++;
            else if(s[i]==')') f--;
            else{
                q.push_back(i);
            }
            if(f<0){
                if(q.size()>0){
                    f++;
                    int pos = q.front();
                    q.pop_front();
                    s[pos] = '(';
                }
                else break;
            }
        }
        if(f<0) {
            cout << "No solution!" << endl;
            continue;
        }
        int x = 0;
        RFOR(i,n-1,0){
            if(f==0) break;
            if(s[i]=='('){
                x--;
                if(x<0) break;
            }
            else if(s[i]=='*') {
                f--;
                x++;
                s[i] = ')';
            }
            else x++;
        }
        if(f>0) {
            cout << "No solution!" << endl;
            continue;
        }
        FOR(i,0,n-1){
            if(s[i]!='*')
                cout << s[i];
        }
        cout << endl;

    }
    return 0;
}

1010

题意
解法
代码

1010

题意
解法
代码
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值