Test 2 for NOIP- Result for Day2

头大

这个暑假完就要去搞NOIP了。。。

暑假55天也就20次测试。。。为防万一我还是每次测试玩都写个总结。。


。。。别说了死的稀里哗啦。。。T1就是我并不怎么懂的动归。。。还有3个月就考试了。。。

Day2 (30/300)

T1 Permut(0/100)

题目描述
求由 1 到 n 一共 n 个数字组成的所有排列中,逆序对个数为 k 的有多少个。

输入格式
第一行为一个整数 T ,为数据组数。
以下 T 行,每行两个整数 n,k,意义如题目所述。

输出格式
对每组数据输出答案对 10000 取模后的结果。

样例数据 1
输入  [复制]

1
4 1
输出

3
备注
对于 30% 的数据,满足:n≤12;
对于所有数据,满足:n≤1000, k≤1000,T≤10。

首先看到逆序对三个字就懵了。。然后根本下不了口。。就直接扔了。。
还是重点补动态规划

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int n,k,t,f[1005][1005];

void solve()
{
    memset(f,0,sizeof(f));
    for(int i=0;i<=n;i++)   f[i][0] = 1;
    for(int j=k;j>=1;j--)   f[0][j] = 1;

    for(int i=1;i<=n;i++)
    for(int j=1;j<=k;j++)
    {
        if(i>j) f[i][j] = (f[i][j-1] + f[i-1][j])%10000;
        else    f[i][j] = (f[i][j-1] + f[i-1][j] - f[i-1][j-i])%10000;
    }

    cout << (f[n][k]-f[n][k-1]+10000)%10000 << endl;
}

int main()
{
    //freopen("permut.in","r",stdin);
    //freopen("permut.out","w",stdout); 

    cin >> t;
    while(t--)  
    {
        cin >> n >> k;

        solve();
    }
}

这题的动归实际上很简单。。。

T2 Beautiful

题目描述
一个长度为 n 的序列,对于每个位置 i 的数 ai 都有一个优美值,其定义是:找到序列中最长的一段 [l,r] ,满足 l≤i≤r,且 [l,r] 中位数为ai(我们比较序列中两个位置的数的大小时,以数值为第一关键字,下标为第二关键字比较。这样的话 [l,r] 的长度只有可能是奇数),r-l+1 就是 i 的优美值。

接下来有 Q 个询问,每个询问 [l,r] 表示查询区间 [l,r] 内优美值的最大值。

输入格式
第一行输入 n 。
接下来 n 个整数,代表 ai。
接下来 Q ,代表有 Q 个区间。
接下来 Q 行,每行两个整数 l,r (l≤r),表示区间的左右端点。

输出格式
对于每个区间的询问,输出答案。

样例数据 1
输入  [复制]

8
16 19 7 8 9 11 20 16
8
3 8
1 4
2 3
1 1
5 5
1 2
2 8
7 8
输出

7
3
1
3
5
3
7
3
备注
【数据规模】
对于 30% 的数据,满足:n,Q≤50;
对于 70% 的数据,满足:n,Q≤2000;
对于所有数据,满足 n≤2000;Q≤100000;ai≤200 。

通过乱做暴枚貌似得了30分?(然而SOJ上全部runtime error不知道为啥)
正解是典型的RMQ,又复习了一遍。

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int n,q;
int a[2005],soi[2005],ans[2005],lok[2005];
int di[2005][2005];

void initi(int l,int r)
{
    for(int i=l;i<=r;i++)
      soi[i] = a[i];
}

void init()
{
    for(int l=1;l<=n;l++)
    for(int r=l;r<=n;r+=2)
    {
        initi(l,r);
        sort(soi+l,soi+r+1);
        ans[soi[(l+r)/2]] = max(ans[soi[(l+r)/2]],r-l+1);
        memset(soi,0,sizeof(soi));
        initi(l,r);
    }
}

int main()
{

    cin >> n;
    for(int i=1;i<=n;i++)
        cin >> a[i];

    init();

    cin >> q;
    while(q--)
    {
        int l,r,dd=0;
        cin >> l >> r;
        for(int i=l;i<=r;i++)
          dd = max(dd,ans[a[i]]);
        cout << dd << endl;
    }
}

注意RMQ与LCA之间的联系,很可能会考

T3 Subset

题目描述
一开始你有一个空集,集合可以出现重复元素,然后有 Q 个操作:

  1. add s
    在集合中加入数字 s 。

  2. del s
    在集合中删除数字 s 。保证 s 存在。如果有多个 s,只删除一个即可。

  3. cnt s
    查询满足 a&s=a 条件的 a 的个数。

输入格式
第一行一个整数 Q 接下来 Q 行,每一行都是 3 个操作中的一个。

输出格式
对于每个 cnt 操作输出答案。

样例数据 1
输入  [复制]

7
add 11
cnt 15
add 4
add 0
cnt 6
del 4
cnt 15
输出

1
2
2
备注
【数据规模】
对于 30% 的数据满足:1≤n≤1000;
对于 100% 的数据满足:1≤n≤200000;0<s<216 。

本来第一眼扫过去觉得就是个哈希表就能解决的事情再加个队列优化下时间就妥妥的了,结果是虽然add,del很快但cnt时即使是队列全部枚举一遍也会gg。。。
正解是分块(?),就是强制压缩询问范围,使得它的时间强制性变成(n*2^8)ms。。。好的又get了一个。。。

MY.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int n, s, cnt = 0;
int stack[200050], hash[70000], check[70000];
char c[5], num[20];

void cint(int aq,int cnt)
{
    int ans=0,i;
    for(i=1;i<=cnt;i++)
    {
        if(hash[stack[i]])
        {
          int h1 = stack[i];
          int h2 = aq;
          if((h1&h2)==stack[i])
            ans += hash[stack[i]];
        }
    }
    cout << ans << endl;
}

bool comp(int a,int b)
{
    return a>b;
}

int init(int cnt,int flag)
{
    sort(stack+1,stack+cnt+1,comp);
    if(flag)    cnt-=1;
    for(int i=1;i<=cnt;i++)
        check[stack[i]] = i;
    return cnt;
}

int main()
{

    int y=0;
    cin >> n;
    while(n--)
    {
        int flag=0;
        scanf("%s",c);  cin >> s;
        if(c[0]=='a')   {hash[s]+=1;if(hash[s]==1)stack[++cnt]=s;}
        else if(c[0]=='d')  {hash[s]-=1;if(hash[s]==0)stack[check[s]]=0,check[s]=0,flag = 1;}
        else if(c[0]=='c')  {cint(s,cnt);y+=1;}
        cnt = init(cnt,flag);
    }

}

过了两个点但完全没有参考价值思路是完全错的。。
神奇的是样例数据全过
网络流我还是渣。。。

STD.CPP

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;

int n,s,a[300][300];
char c[5];
int main()
{
    memset(a,0,sizeof(a));

    cin >> n;
    while(n--)
    {
        scanf("%s",c);  cin >> s;

        int a1 = s/256;
        int a2 = s%256;
        int ans = 0;

        if(c[0]=='a')   for(int i=0;i<=255;i++) if((a1&i)==a1)  a[i][a2]+=1;
        if(c[0]=='d')   for(int i=0;i<=255;i++) if((a1&i)==a1)  a[i][a2]-=1; //<=这里前两步是为了直接得结果,访问时直接找
        if(c[0]=='c')   for(int i=0;i<=255;i++) if((i&a2)==i)   ans += a[a1][i];

        if(c[0]=='c')   cout << ans << endl;
    }
}

代码同样短的吓人。。只是有点绕

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值