slove  2/12

rank  358

补题   5/10

---------------------------------------------------

link

6635 Nonsense Time

每次加入一个数字后求最长上升子序列长度

题目保证数据随机生成,就是说只有  的概率会选到在最上上升子序列上的值,所以每次暴力跑,存下最长上升子序列的值就可以

#include <bits/stdc++.h>
#define sc scanf
#define pr printf
using namespace std;
int n, t;
int a[50005], b[50005];
bool vis[50005];
bool lis[50005];
int num[50005];
int ans;
int res[50005];

int c[50005];
int lowbit(int k)
{
    return k & (-k);
}
void update(int k, int x)
{
    while (k <= n)
    {
        c[k] = max(c[k], x);
        k += lowbit(k);
    }
}
int query(int k)
{
    int ans = 0;
    while (k)
    {
        ans = max(ans, c[k]);
        k -= lowbit(k);
    }
    return ans;
}

void find()
{
    ans = 0;
    memset(c, 0, sizeof(int) * (n + 1));
    for (int i = 1; i <= n; i++)
        lis[i] = false;
    for (int i = 1; i <= n; i++)
        num[i] = 0;
    for (int i = 1; i <= n; i++)
    {
        if (vis[i] == true)
            continue;
        int val = query(a[i] - 1) + 1;
        ans = max(ans, val);
        num[i] = val;
        update(a[i], val);
    }
    int val = ans;
    for (int i = n; i >= 1; i--)
    {
        if (num[i] == val)
        {
            lis[i] = true;
            val--;
            if (!val)
                break;
        }
    }
}
int main()
{
    int T;
    sc("%d", &T);
    while (T--)
    {
        sc("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            vis[i] = false;
            sc("%d", &a[i]);
        }
        for (int i = 1; i <= n; i++)
            sc("%d", &b[i]);
        find();
        res[n] = ans;
        for (int i = n; i > 1; i--)
        {
            vis[b[i]] = true;
            if (lis[b[i]] == true)
                find();
            res[i - 1] = ans;
        }
        for (int i = 1; i <= n; i++)
            printf("%d%c", res[i], i == n ? '\n' : ' ');
    }
}

6638 Snowy Smile

区间最大子段和

https://blog.csdn.net/qq_41608020/article/details/98845958

6639 Faraway

给你n个士兵的xi,yi,ki,ti,满足题目中给出的方程
(|xi-xe|+|yi-ye|) mod ki=ti.
求有多少个(xe,ye)满足这n个方程 


首先想到去绝对值,有绝对值肯定是不好算的.
n个士兵将平面分为了n^2个块
对于每一块内暴力计算. 
由于k只能为2,3,4,5,所以只需要枚举每一块内的60个(其实可以更小)即可,
后面的和%60后的答案是一样的 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 1e2+5;
ll n,m,x[MAX],y[MAX],k[MAX],t[MAX];
vector<ll> list1,list2;
bool check(ll X,ll Y){
    for(int i=1;i<=n;++i)
        if((abs(x[i]-X)+abs(y[i]-Y))%k[i]!=t[i])
            return false;
    return true;
}
ll calc(ll a,ll b){
    return (b-a-1)<0?0:(b-a-1)/60+1;
}
void solve(){
    int T;
    scanf("%d",&T);
    while(T--){
        list1.clear(); list2.clear();
        scanf("%lld%lld",&n,&m);
        list1.emplace_back(0);
        list1.emplace_back(m+1);
        list2.emplace_back(0);
        list2.emplace_back(m+1);
        for(int i=1;i<=n;++i){
            scanf("%lld%lld%lld%lld",&x[i],&y[i],&k[i],&t[i]);
            list1.emplace_back(x[i]);
            list2.emplace_back(y[i]);
        }
        sort(list1.begin(),list1.end());
        sort(list2.begin(),list2.end());
        ll ans=0;
        for(int i=0;i<list1.size()-1;++i)
            if(list1[i]<list1[i+1])
                for(int j=0;j<list2.size()-1;++j)
                    if(list2[j]<list2[j+1])
                        for(ll k1=0;k1<60;++k1)
                            for(ll k2=0;k2<60;++k2)
                                if(check(list1[i]+k1,list2[j]+k2))
        ans+=calc(list1[i]+k1,list1[i+1])*calc(list2[j]+k2,list2[j+1]);
        printf("%lld\n",ans);
    }
}
int main(void)
{
    solve();
    return 0;
}

6641 TDL

给两个数字 m,k,,已知 n 满足 ,其中  是大于n的第m小的质数,例如 f(5,1)=6 and f(5,5)=11。求 n 的最小值

已知 m 的范围小于100,所以  一定小于等于1000,所以枚举他的值,通过等式来算出 n 的值,再判断一下是否合法,合法就更新最小值,否则无解。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2005;
ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}
ll f(ll n, int m)
{
    for (ll i = n + 1; true; i++)
    {
        if (gcd(n, i) == 1)
        {
            m--;
            if (m == 0)
                return i;
        }
    }
}
int main()
{
    int T;
    sc("%d", &T);
    while (T--)
    {
        ll k;
        int m;
        sc("%lld%d", &k, &m);
        ll ans = 2e18;
        for (ll i = 0; i <= 1000; i++)
        {
            ll fn = i;
            ll n = k ^ i;
            if (n == 0)
                continue;
            if (f(n, m) - n == fn)
            {
                ans = min(ans, n);
            }
        }
        if (ans == 2e18)
            ans = -1;
        pr("%lld\n", ans);
    }
}

6645 Stay Real

签到,有 n 个结点的完全二叉树,每个点对应一个权值,每次只能取叶子结点,两个人在树上取数,求两个人取的数的大小。

#include <bits/stdc++.h>
#define ll long long
#define sc scanf
#define pr printf
using namespace std;
const int MAXN = 2005;
bool vis[100005];
ll h[100005];
auto cmp = [](int q, int w) {
    return h[q] < h[w];
};
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        int n;
        sc("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            sc("%lld", &h[i]);
            vis[i] = false;
        }
        vis[n + 1] = true;
        priority_queue<int, vector<int>, decltype(cmp)>q(cmp);
        for (int i = n; i >= 1; i--)
            if (i * 2 > n)
                q.push(i);
        int op = 0;
        ll ans1 = 0, ans2 = 0;
        while (!q.empty())
        {
            int t = q.top();
            q.pop();
            vis[t] = true;
            int fa = t / 2;
            while (fa)
            {
                if (vis[2 * fa] == true && vis[2 * fa + 1] == true)
                {
                    q.push(fa);
                    fa /= 2;
                }
                else
                    break;
            }
            if (!op)
                ans1 += h[t];
            else
                ans2 += h[t];
            op ^= 1;
        }
        printf("%lld %lld\n", ans1, ans2);
    }
}