20181030晚(數學小問題+位運算小問題+亂搞DP+莫隊)

T1 排序

小明班裏一共 𝑁名同 學,小明這次考試的不錯他知道了多少分,以及班級裏的同學一共考了幾分,考試滿分100分,同分數可並列排名,求小明最高可以多少名,最低可以多少名
N<=1e5,N<=1e5,

T2 位運算

給定 𝑁個非負整 數 ,每次你可以選擇兩個數 𝑎,𝑏,,將其中一個數變爲 𝑎 𝑎𝑛𝑑 𝑏,另一個變 成𝑎 𝑜𝑟 𝑏,你可以進行多次操作, 任何時候都可以停止。
N<=1e5,N<=1e5,

T3 最長不下降子序列

給你一個正整數 N,SR希望你統計所有的長度恰好爲n的01序列的最長不下降子之序列之和
N<=200N<=200

T1

題意概括:給出n個人得分總量,給出一個人分數p,求這個人能在在這些人中最高拍多少,最低排多少

這道題好惡心,一開始推了一會兒還很得意以爲又是一道水題(本來就是一道水題),後來發現推錯了。。。
於是又推推推,
1.讓他最高分反正就是讓pp分的人儘量多(儘量同分能使排名更高,好讓比p低的更多)
2.讓他最低分,就是讓p+1p+1的人儘量多,(比它高一點又不佔太多分數,)

#include<bits/stdc++.h>
using namespace std;
int n,sump,pe,highrnk,lowrnk,mid;
inline void low()
{
  int sumyu=(n+1)*pe;
  if (sumyu<=sump) {lowrnk=pe+1; return;}
  int yu=sumyu-sump;
  int bu=yu/n;
  int yu1=yu%n;
  if (yu1>=1) {lowrnk=pe-bu; return;}
  lowrnk=pe-bu-1;
}
inline void high()
{
	int sumyu=n*pe;
	if (sumyu>=sump) {highrnk=1; return;}
    int yu=sump-sumyu;
    int bu=yu/(100-n);
    int yu1=yu%(100-n);
    if (yu1>=1) {highrnk=bu+2; return;}
    highrnk=bu+1;
}
int main()
{
	scanf("%d%d%d",&pe,&n,&sump);
	pe--;
	sump-=n;
	mid=sump/pe;
	low();
    high();
    if (n==100) highrnk=1; 
    printf("%d %d",highrnk,lowrnk);
}

對了 p=100分的時候要特判

T2
題意概括:額,我覺得題目講的挺清楚了
首先肯定是每個合併過後的數,二進制1越多越好,那麼我們統計每一個數所有位數上1的個數,因爲1or0=11or0=1,1and1=11and1=1
所以每一位上只要還存在1,肯定就能合併

#include<bits/stdc++.h>
const int maxn=100007;
using namespace std;
long long n,m;
long long tong[maxn];
int main()
{
    scanf("%lld",&n);
    long long ans=0;
    for (long long i=1; i<=n; i++)
      {
        long long x;
        scanf("%lld",&x);
        for (long long j=0; j<=20; j++)
          if(x&(1<<j)) tong[j]++;    
      }
    for (long long i=1; i<=n; i++)
      {
        long long x=0;
        for (long long j=0; j<=20; j++)
            if (tong[j])
              {
                tong[j]--;
                x+=(1<<j);
              }
        ans+=x*x;
    }
    printf("%lld\n",ans);
}

T3
題意概括:如題,長度爲n的所有01串中最長不下降子序列長度之和

O(n3)O(n^3):我們不妨考慮這個狀態 𝑓[𝑖][𝑗][𝑘]表示長度爲 𝑖,以 0結尾的最長不下降子序列度爲 𝑗,以 1結尾的最長不下降子序列爲 𝑘的 01序列數有多少。
由於不方便考慮每個狀態那些轉移得到。那我們不妨考慮對於每個狀態可以轉移到那些狀態,對於 𝑓[i][𝑗][𝑘]當前的狀態來說,有兩種轉移方法第一是在後面添加個 0,那 麼轉移到的狀態是 𝑓[𝑖+1][𝑗+1][𝑘],第二種是在後面添加一個 1,,轉移到的狀態是 𝑓[𝑖+1][𝑗][𝑚𝑎𝑥(𝑗,𝑘)+1]。
我們再考慮對答案的貢獻, 𝑓[𝑛][𝑗][𝑘]對答案的貢獻是 𝑚𝑎𝑥(𝑗,𝑘)∗𝑓[𝑛][𝑗][𝑘]。

#include<iostream>
using namespace std;
int n;
long long f[205][205][205];
#define MOD 1000000007
int main()
{
    cin>>n;
    f[1][1][0]=1;
    f[1][0][1]=1;
    long long ans=0;
    for (int i=1;i<=n;i++)
    {
        for (int j=0;j<=i;j++)
        {
            for (int k=0;k<=i;k++)
            {
                f[i+1][j+1][k]+=f[i][j][k];
                f[i+1][j][max(j,k)+1]+=f[i][j][k];
                f[i+1][j+1][k]%=MOD;
                f[i+1][j][max(j,k)+1]%=MOD;
            }
        }
    }
    for (int j=0;j<=n;j++)
    for (int k=0;k<=n;k++)
    {
        ans=(ans+(long long)max(j,k)*f[n][j][k])%MOD;
    }
    cout<<ans;
}

O(n2)O(n^2):
每次往01串前加個1或者0
找規律
可得

#include<bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define int long long

const int bs=220; 
int g[210][230],f[220],n;

int p2(int k){
    if(k==0) return 1;
    int t=p2(k/2);
    return (t*t%mod*((k&1)?2:1))%mod;
}

signed main(){
    cin>>n;
    f[1]=2;
    g[1][0+bs]=g[1][1+bs]=1;
    for(int i=2;i<=n;i++){
        f[i]=(2*f[i-1]%mod+p2(i-1)+g[i-1][1+bs])%mod;
        g[i][1+bs]=(g[i-1][1+bs]+g[i-1][bs])%mod;
        for(int j=0;j>=1-i;j--)
            g[i][j+bs]=(g[i-1][j+1+bs]+g[i-1][j-1+bs])%mod;
    }
    cout<<f[n]<<endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章