机试(2017 cs se)

news2024/11/19 1:45:37

2017计算机系夏令营 

题解参考: 

2017 华东师范计算机系暑期夏令营机考

A. 不等式

Problem #3304 - ECNU Online Judge

有点像贪心算法

选一个刚刚好在条件范围里的b[i]作为候选,【这个“刚刚好”是指选一个符合这个条件的最极限的值】

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
     string x;string op[203];int a[203];int b[203];
     int n;cin>>n;
     for(int i=0;i<n;i++){
          cin>>x>>op[i]>>a[i];
          if(op[i]==">")b[i]=a[i]+1;//b[i]是ok的数字,a[i]是题目里的数字
          else if(op[i]=="<")b[i]=a[i]-1;
          else b[i]=a[i];
     }
     int ans=0;
     for(int i=0;i<n;i++){//遍历每一个b[i]
          int cnt=0;
          for(int j=0;j<n;j++){//判断第j个命题是否能满足
               if(op[j]=="="&&b[i]==a[j])cnt++;
               else if(op[j]==">="&&b[i]>=a[j])cnt++;
               else if(op[j]=="<="&&b[i]<=a[j])cnt++;
               else if(op[j]==">"&&b[i]>a[j])cnt++;
               else if(op[j]=="<"&&b[i]<a[j])cnt++;
          }
          ans=max(ans,cnt);//取cnt的最大值
     }
     cout<<ans<<'\n';
     return 0;
}

B. 1 的个数最多的整数

Problem #3303 - ECNU Online Judge

暴力

首先把a变成二进制,之后从低位到高位遍历,如果这一位是0,那么看看能不能变成1(变成1会不会超过b,如果不会就记录)

为什么要从低位开始遍历?如果从高位开始遍历的话,有些低位的1就选不到了

注意:不要新开一个ans=0,之后判断条件为ans+tmp<=b;因为假设a=0001,b=1001(左边是低位)这样ans=1110的时候(这时ans<b)因为a[3]=1,所以ans+8>b就不合法了

所以直接在a上面做加法就可以了

注意:如果用数组存放a和b的二进制,记得定义在T次循环的内部

注意:(1<<i)默认是int,要(long long)1<<i 才是long long的状态

代码

#include<bits/stdc++.h>
using namespace std;
long long a,b;long long tmpa,tmpb;
int main()
{
     int T;cin>>T;
     for(int t=1;t<=T;t++){
          int sa[70]={0};int sb[70]={0};
          cin>>a>>b;
          int st1=0,st2=0;
          tmpa=a,tmpb=b;
          while(tmpa){
               sa[st1]=tmpa%2;tmpa/=2;st1++;
          }
          while(tmpb){
               sb[st2]=tmpb%2;tmpb/=2;st2++;
          }
          long long tmp=1;
          for(int i=0;i<st2;i++){
               if(sa[i]==0&&a+tmp<=b)//表示可以变成1看看
                         a+=tmp;
               tmp=tmp*2;

          }
          cout<<"Case "<<t<<": "<<a<<'\n';
     }
}

C. 打印

打印n个相同的字符,插入或删除一个花费x,复制花费y

动态规划

首先dp[0]=0,dp[1]=x,dp[2]=min(dp[1]+x,dp[1]+y),dp[3]=min(dp[2]+x,dp[1]+y+x)

当 i 为双数时,dp[i]=min(dp[i-1]+x,dp[i/2]+y)

当 i 单数时,dp[i]=min(dp[i-1],dp[(i-1)/2]+y,dp[(i+1)/2]+y)+x

注意:i为单数的时候,可以先(i-1)/2,再插入一个x;也可以(i+1)/2,再删除一个x

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll dp[10000007];
int main()
{
     ll n,x,y;cin>>n>>x>>y;dp[1]=x;
     for(int i=2;i<=n;i++){
          if(i%2)dp[i]=min(dp[i-1],min(dp[(i-1)/2]+y,dp[(i+1)/2]+y))+x;
          else dp[i]=min(dp[i-1]+x,dp[i/2]+y);
     }
     cout<<dp[n];
     return 0;
}

D. 十亿分考

Problem #3305 - ECNU Online Judge

完全不会orz

看标答有用随机数的,有用连分数的

代码 随机数

随机 重要的是eps来确定精度,但是如果精度太高会超时(例如1e-16),如果精度太低会WA(例如5e-16)

for循环来鲁棒之类的(?)

#include<bits/stdc++.h>
using namespace std;
int main()
{
     long double eps=5e-16;
     long double a;cin>>a;
     srand(time(0));
     long long p,q;
     while(1){
          q=rand()%1000000001;
          long long mid=q*a;
          for(long long p=mid-15;p<=mid+15;p++){
               long double chp=(long double)p/q-a;
               if(fabs(chp)<eps){
                    cout<<p/__gcd(p,q)<<" "<<q/__gcd(p,q)<<'\n';
                    return 0;
               }
          }
     }

}

代码 连分数

题解 2017 华东师范计算机系暑期夏令营机考_十亿分考​​​​​​

连分数OI wiki连分数 - OI Wiki (oi-wiki.org) 

任何有理数都可以精确地以两种方式表示为连分数:

Q:怎么从一个小数a得到a0,a1,a2,……an的表示呢?

a0=(int)1/a        r0=(double)1/a0-(double)a0

a1=(int)1/r0       r1=(double)1/a1-(double)a1

 ……

an=(int)1/rn-1    rn=(double)1/an-(double)an

Q:怎么从a0,a1,a2序列中算出原来的分子分母?

这就是从最小的分式开始向上,最小的分式看作0,也就是p=0,q=1;向上p=1,q=cnt[n-1];之后来到了新的分式,这时上下乘以q通分,分母是cnt[n-2]*q+原来的分子p,因为新来的分子永远是1,所以新的p=q;最后遍历完毕得到p和q

注意当输入的a为0的时候要特判!!!

#include<bits/stdc++.h>
using namespace std;
vector<long long> cnt;
long double cal(){
     long double tmp=0;
     for(int i=cnt.size()-1;i>=0;i--){
          tmp+=cnt[i];
          tmp=1/tmp;
     }
     return tmp;
}
int main()
{
     long double a;cin>>a;
     if(!a){cout<<0<<" "<<1<<'\n';return 0;}
     long double tmp=a;
     while(1){
          long long v=1/tmp;
          cnt.push_back(v);
          tmp=(long double)1/tmp-(long double)v;
          if(fabs(a-cal())<4e-16)break;
     }
     long long p=0,q=1;
     for(int i=cnt.size()-1;i>=0;i--){
          p+=cnt[i]*q;
          swap(p,q);
     }
     cout<<p/__gcd(p,q)<<" "<<q/__gcd(p,q)<<'\n';
     return 0;
}

E. 有钱人买钻石

Problem #3306 - ECNU Online Judge

如果是动态规划的话 p是1e8 太大了

dfs做,重点在于

1. 从单价为1的硬币开始dfs

2. 硬币枚数从高到低枚举因为方案可行后就可以退出来了

3. high的值为(p-sum)/y[id],low的值为high-25

【注意 int low=max(0,(p-sum)/y[id]-25); 的写法是错误的,因为n[id]可能没有那么多】所以用high

为什么low是high-25?

因为例如 30 29 0 0 0

id=0的时候,high=29,low=4

        遍历到low=4了

这时来想一想为什么要从大到小遍历呢?——因为要减少小的给大的让位来满足p的需求

但是我让了25个位置了,就算有一个25来补足,也只是抹平抵消而已,并没有加上什么,说明方案就不可行,

        所以low=high-25就return了

【但注意for循环的终止条件写  i>low 是错误的,因为low可能>high-25,只是因为不能为负,所以low=0;终止条件要写i>=low  】

代码 dfs

#include<bits/stdc++.h>
using namespace std;
int p,n[5],ans=-1;
int y[]={1,5,10,25};
bool dfs(int sum,int id,int num){//已经兑换的额度,第几个硬币,已经兑换的个数
     if(sum>p)return 0;
     if(id==4){
          if(sum==p){
               ans=max(ans,num);
               return 1;
          }
          return 0;
     }
     int high=min(n[id],(p-sum)/y[id]);
     int low=max(0,high-25);//这个最低也挺妙的
     for(int i=high;i>=low;i--){//注意从高到低枚举,成功了就可以break了
          if(dfs(sum+y[id]*i,id+1,num+i))return 1;
     }
     return 0;
}
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);
     cin>>p>>n[0]>>n[1]>>n[2]>>n[3];
     dfs(0,0,0);
     if(ans==-1){cout<<"Impossible\n";return 0;}
     cout<<ans<<'\n';
     return 0;
}

F. 送分题

Problem #3307 - ECNU Online Judge

离线 排序,以右端点为第一排序,左端点为第二排序来对区间排序

用map来记录的话,就是种类:

个数

右指针移动,如果是增加的话,等于2的时候种类总和+1,之后移到右端点位置后,移动左指针,如果是减少的话如果减了且减到1了,种类总和-1,直到移动到左端点位置结束

有的时候,不止向右,还会向左,例如3 3和1 5,这个时候就l就像r一样就可以了

代码 TLE

虽然是TLE(4/10)代码,但是写代码时注意区间的开闭问题,就比如下面的代码中,

当 l 的起点是1,减法的时候 mp[a[l]]先减,之后再l--,而且循环条件为l<now.l,这就说明now.l这个点还没有被剪掉;r 的起点是0时,加法的时候,先r++,mp[a[r]]再加,循环条件为r<now.r,这就说明now.r在循环结束的时候已经被加上了

#include<bits/stdc++.h>
using namespace std;
struct node{
     int l,r,id;

};
bool cmp(node a,node b){
     if(a.r!=b.r)return a.r<b.r;
     return a.l<b.l;
}
node qu[500005];//左端点 右端点 id
int a[500005]; int ans[500005];
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
     int N,Q;cin>>N>>Q;
     for(int i=1;i<=N;i++)cin>>a[i];
     for(int i=0;i<Q;i++){
          cin>>qu[i].l>>qu[i].r;
          qu[i].id=i;
     }
     sort(qu,qu+Q,cmp);
     int sum=0;
     map<int,int>mp;int l=1,r=0;
     for(int i=0;i<Q;i++){
          node now=qu[i];
          while(r<now.r){
               r++;mp[a[r]]++;if(mp[a[r]]==2)sum++;
               if(mp[a[r]]==3)sum--;
          }
          while(l<now.l){
               mp[a[l]]--;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==1)sum--;l++;
          }
          while(l>now.l){
               l--;mp[a[l]]++;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==3)sum--;
          }
          ans[now.id]=sum;
     }
     for(int i=0;i<Q;i++)cout<<ans[i]<<'\n';
     return 0;
}

代码 莫队 分块排序 TLE

上面的代码时间复杂度最差还是O(mn),所以用莫队

莫队的形式

分块,块的大小是根号n,对于l在同一个块内的,按照r的大小排序,否则就按照块号排序

TLE(8/10)【unordered map使测试7通过了,用node或者把三者拆开来排序的效率都是一样的】

#include<bits/stdc++.h>
using namespace std;
unordered_map<int,int>mp;
int a[500005];int ans[500005];
int N,Q;int block;
struct node{
     int l,r,id;
};
node qu[500005];//左端点 右端点 id
bool cmp(node a,node b){
     if(a.l/block==b.l/block)//如果是在一个块内的
     return a.r<b.r;
     return a.l/block<b.l/block;//不在一个块内的按照块号分
}
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
     cin>>N>>Q;block=sqrt(N);
     for(int i=1;i<=N;i++)cin>>a[i];
     for(int i=0;i<Q;i++){
          cin>>qu[i].l>>qu[i].r;
          qu[i].id=i;
     }
     sort(qu,qu+Q,cmp);
     int sum=0;
     int l=1,r=0;
     for(int i=0;i<Q;i++){
          node now=qu[i];
          while(r>now.r){
               mp[a[r]]--;if(mp[a[r]]==2)sum++;
               if(mp[a[r]]==1)sum--;r--;
          }
          while(r<now.r){
               r++;mp[a[r]]++;if(mp[a[r]]==2)sum++;
               if(mp[a[r]]==3)sum--;
          }
          while(l<now.l){
               mp[a[l]]--;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==1)sum--;l++;
          }
          while(l>now.l){
               l--;mp[a[l]]++;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==3)sum--;
          }
          ans[now.id]=sum;
     }
     for(int i=0;i<Q;i++)cout<<ans[i]<<'\n';
     return 0;
}

代码 莫队 分块 离散化 AC

记录思考了两天的SB时刻

注意看,虽然a[i]的范围是0~1e9,但是它只有5e6个数字,这个时候把a离散化以下,就可以把a在区间内的个数收纳在5e6的数组mp内,其中mp的序号是a[i]数值的种类,mp的值是a[i]在区间里出现的个数

所以离散化,这样就不会在while里面的map的加减花费太多时间(?),于是就AC了!

#include<bits/stdc++.h>
using namespace std;
int mp[500005];
int a[500005];int ans[500005];
int N,Q;int block;int tot=0;
struct node{
     int l,r,id;
};
unordered_map<int,int>num;
node qu[500005];//左端点 右端点 id
bool cmp(node a,node b){
     if(a.l/block==b.l/block)//如果是在一个块内的
     return a.r<b.r;
     return a.l/block<b.l/block;//不在一个块内的按照块号分
}
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
     cin>>N>>Q;block=sqrt(N);
     for(int i=1;i<=N;i++){
          cin>>a[i];
          if(num.count(a[i])==0)num[a[i]]=tot++;//离散化
          a[i]=num[a[i]];
     }
     for(int i=0;i<Q;i++){
          cin>>qu[i].l>>qu[i].r;
          qu[i].id=i;
     }
     sort(qu,qu+Q,cmp);
     int sum=0;
     int l=1,r=0;
     for(int i=0;i<Q;i++){
          node now=qu[i];
          while(r>now.r){
               mp[a[r]]--;if(mp[a[r]]==2)sum++;
               if(mp[a[r]]==1)sum--;r--;
          }
          while(r<now.r){
               r++;mp[a[r]]++;if(mp[a[r]]==2)sum++;
               if(mp[a[r]]==3)sum--;
          }
          while(l<now.l){
               mp[a[l]]--;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==1)sum--;l++;
          }
          while(l>now.l){
               l--;mp[a[l]]++;if(mp[a[l]]==2)sum++;
               if(mp[a[l]]==3)sum--;
          }
          ans[now.id]=sum;
     }
     for(int i=0;i<Q;i++)cout<<ans[i]<<'\n';
     return 0;
}

2017软件系夏令营

3296. 2333

Problem #3296 - ECNU Online Judge

首先魔法石可以变成2或者3或者23,问最多能组多少个2333

那最省的组成方法就是三个石头组成一个2333,所以整除3就好了

重点:做题时要看清题目,一开始看成多少种排列组合了,仔细看了看之后才发现是能组多少个

注意:输出一个答案后要换行

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);
     int T;cin>>T;int a;
     while(T--){
          cin>>a;
          cout<<a/3<<'\n';
     }
     return 0;
}

3297. 铺瓷砖

Problem #3297 - ECNU Online Judge

就方案数 和 1的个数

乍一看以为是状态压缩dp,吓死

代码 dfs

方案数就是最后return的时候如果是合理的,那么方案数+1;1的个数就是return的时候加上该方案1的个数

#include<bits/stdc++.h>
using namespace std;
int maxx=0,all=0,ans=0;
void dfs(int sum,int pre,int num){//sum是已经铺的,pre是前一个,num是有多少个1
     if(sum>=all){
          if(sum==all){maxx=maxx+num;ans++;}
          return;
     }
     if(pre==1){
          dfs(sum+2,2,num);dfs(sum+3,3,num);
     }
     else if(pre==2){
          dfs(sum+1,1,num+1);dfs(sum+3,3,num);
     }
     else if(pre==3){
          dfs(sum+1,1,num+1);dfs(sum+2,2,num);
     }
     else{
          dfs(sum+1,1,num+1);dfs(sum+2,2,num);dfs(sum+3,3,num);
     }
}
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);
     int T;cin>>T;string s;
     while(T--){
          maxx=0;ans=0;cin>>all;
          dfs(0,0,0);
          cout<<ans<<'\n'<<maxx<<'\n';
     }
     return 0;
}

代码 线性dp

用两个dp来表示,一个是方案数,一个是1的个数

二维dp,第一位是长度,第二位是现在放入的;

初始化dp[1][1]=1,dp[2][2]=1,dp[3][3]=1,dp[3][2]=1,dp[3][1]=1;

状态转移dp[i][1]=dp[i-1][2]+dp[i-1][3],dp[i][2]=dp[i-2][1]+dp[i-2][3],dp[i][3]=dp[i-3][1]+dp[i-3][2];

方案数总数是dp[n][1]+dp[n][2]+dp[n][3]

如何得到1的个数?——想不出来怎么从上面的dp中找到1的个数的答案,所以就新开了一个one

状态转移:

one[n][1]中1的个数是从one[n-1][2]+1和one[n-1][3]+1得到的;【但是这一步是有问题的】

one[n][2]中1的个数是从one[n-2][1]和one[n-2][3]得到的;

one[n][3]中1的个数是从one[n-3][1]和one[n-3][2]得到的

初始化 one[1][1]=1,one[3][1]=1,one[3][2]=1

Q:上面划线的步骤为什么有问题呢?

因为不确定one[n-1][2]和one[n-1][3]是否合法,例如one[4][2]就不合法,如果按照上面的步骤就变成了one[5][1]=one[4][2]+one[4][3]+2,这是不合法

并且,只要是dp[i-1][2]和dp[i-1][3]存在,所有的它们后面加上1都可以增加one[i][1]的个数

例如,下面是dp[6][1]的方案,从中可以看到dp[5][2]=2,dp[5][3]=1

2 3 1

3 2 1

2 1 2 1

从上面可以看到1放在了所有dp[i-1][2]和dp[i-1][3]方案的后面

Q:应该怎么改进呢?

状态方程应该变为one[i][1]=one[i-1][2]+one[i-1][3]+dp[i-1][2]+dp[i-1][3];

#include<bits/stdc++.h>
using namespace std;
int maxx=0,n=0;
int dp[35][4];
int one[35][4];
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);
     int T;cin>>T;string s;
     while(T--){
          memset(dp,0,sizeof(dp));cin>>n;
          dp[1][1]=1,dp[2][2]=1,dp[3][3]=1,dp[3][2]=1,dp[3][1]=1;
          one[1][1]=1,one[3][1]=1,one[3][2]=1;
          for(int i=4;i<=n;i++){
               dp[i][1]=dp[i-1][2]+dp[i-1][3];
               dp[i][2]=dp[i-2][1]+dp[i-2][3];
               dp[i][3]=dp[i-3][1]+dp[i-3][2];
               one[i][1]=one[i-1][2]+one[i-1][3]+dp[i-1][2]+dp[i-1][3];
               one[i][2]=one[i-2][1]+one[i-2][3];
               one[i][3]=one[i-3][1]+one[i-3][2];
          }
          cout<<dp[n][1]+dp[n][2]+dp[n][3]<<'\n'<<one[n][1]+one[n][2]+one[n][3]<<'\n';

     }
     return 0;
}

不过既然想到状压dp了,那就来一道状压铺瓷砖吧

补充:状压dp

291. 蒙德里安的梦想

参考题解:AcWing 291. 蒙德里安的梦想

题意:求把N×M的棋盘分割成若干个1×2 的的小长方形,有多少种方案。1≤N,M≤11

思路:总的方案数就等于摆完所有横向长方形的方案数。所以,我们只用考虑如何枚举横向长方形的摆放即可

3298. 排队买夜宵

Problem #3298 - ECNU Online Judge

做了那么久 终于有一道我一看就会的题了qwq

和 合法括号序列判断 一模一样的题

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
     ios::sync_with_stdio(0);cin.tie(0);
     int T;cin>>T;string s;
     while(T--){
          stack<char>st;
          cin>>s;
          for(int i=0;s[i];i++){
               if(st.empty())st.push(s[i]);
               else if(st.top()=='1'&&s[i]=='0')st.pop();
               else if(st.top()=='0'&&s[i]=='1')st.pop();
               else st.push(s[i]);
          }
          cout<<st.size()<<'\n';
     }
     return 0;
}

9. Alice and A simple problem

Problem #9 - ECNU Online Judge

简单模拟

代码 

#include<bits/stdc++.h>
using namespace std;
int dp[101][101],cnt=0;
int main()
{
     int m,n;cin>>m>>n;
     for(int i=0;i<n;i++){//行
          for(int j=0;j<m;j++){
               dp[j][i]=++cnt;
          }
     }
     for(int i=0;i<m;i++){
          for(int j=0;j<n;j++){
               cout<<dp[i][j];
               if(j!=n-1)cout<<' ';
          }
          cout<<'\n';
     }
     return 0;
}

11. Cacey and Calphabet

找到最大上升子序列的长度?

代码 vector+lower_bound Onlogn

其实就是贪心算法

#include<bits/stdc++.h>
using namespace std;
vector<int> v;
int main()
{
    string s;cin>>s;
     for(int i=0;s[i];i++){
          if(v.empty())v.push_back(s[i]-'a');
          else{
               int tmp=lower_bound(v.begin(),v.end(),s[i]-'a')-v.begin();
               if(tmp==v.size())v.push_back(s[i]-'a');
               else v[tmp]=s[i]-'a';
          }
     }
     cout<<26-v.size();
     return 0;
}

代码 dp On^2

dp[i]表示以i为结尾的最

首先找到s[j]小于s[i]且dp[j]最大的,dp[i]=dp[j]+1

#include<bits/stdc++.h>
using namespace std;
vector<int> v;
int dp[55];
int main()
{
     string s;cin>>s;int n=s.size();
     for(int i=0;i<n;i++){
          int maxx=0;
          for(int j=0;j<i;j++){
               if(s[j]<s[i]&&dp[j]>maxx)maxx=dp[j];
          }
          dp[i]=maxx+1;
     }
     cout<<26-dp[n-1];
}

代码 树状数组 nlogn

树状数组的目的在于找到 i 之前的s[j]<s[i]中的最大值

所以可以以j 和 i为序,换句话说,树状数组的序号只要26个就可以了

一边遍历,一边更新树状数组

#include<bits/stdc++.h>
using namespace std;
# define lowbit(x) x&(-x)
int a[30];
int query(int x){//查询是向下的,比如9的话要查a[8]和a[9]
     int ma=0;
     for(;x;x-=lowbit(x))
          ma=max(ma,a[x]);
     return ma;
}
void add(int x,int k){//更新是向上的,比如更新了a[3],a[4]也要更新
     for(;x<=26;x+=lowbit(x)) 
         a[x]=max(a[x],k);
     return;
}
int main()
{
     string s;cin>>s;int n=s.size();int ans=0;
     for(int i=0;s[i];i++){
          int ma=query(s[i]-'a'+1-1);//+1是因为树状数组是从1开始的,-1是因为s[j]<s[i]
          add(s[i]-'a'+1,ma+1);
          ans=max(ans,ma+1);
     }
     cout<<26-ans;
}

3299. 主色调

Problem #3299 - ECNU Online Judge

时限是3s,暴力做法

在遍历区间的时候,第二层循环来了一个颜色,这个颜色出现次数++,更新其中出现次数最多的颜色的最小的序号【这个用一个if条件判断就可以达到了】

其实就是很普通的模拟题

代码

#include<bits/stdc++.h>
using namespace std;
int a[5003];
int main()
{
     int n;
     while(cin>>n){
          vector<int> ans(n+1,0);
          for(int i=0;i<n;i++)cin>>a[i];
          for(int i=0;i<n;i++){
               vector<int>v(n+1,0);int maxid=0,maxxv=0;
               for(int j=i;j<n;j++){
                    v[a[j]]++;
                    if(maxxv<v[a[j]]||(maxxv==v[a[j]]&&maxid>a[j])){
                         maxid=a[j];maxxv=v[a[j]];
                    }
                    ans[maxid]++;
               }
          }
          for(int i=1;i<=n;i++){
               cout<<ans[i];if(i!=n)cout<<' ';
          }
          cout<<'\n';
     }
}

3300. 奇数统计

Problem #3300 - ECNU Online Judge

不会做,写一个暴力,TLE了

#include<bits/stdc++.h>
using namespace std;
int a[100005];
long long C(long long m,long long n){
     long long g=1,d=1;
     for(int i=1;i<=m-n;i++){
          g=g*(i+n);
          d=d*i;
     }
     return g/d;
}
int main()
{
     int n,T;cin>>T;
     while(T--){
          cin>>n;int ans=0;
          for(int i=0;i<n;i++)cin>>a[i];
          for(int i=0;i<n;i++){
               for(int j=0;j<n;j++){
                    if(a[i]<a[j])continue;
                    if(C(a[i],a[j])&1)ans++;
               }
          }
          cout<<ans<<'\n';
     }
}

参考

题解:EOJ-3300 奇数统计(高维前缀和)

卢卡斯定理:算法学习笔记(25): 卢卡斯定理 

组合数的奇偶判断:组合数奇偶性的判断(附证明)

题解

前置知识:n!中含有2因子的个数等于(n-它的二进制形式中1的个数)

Q:为什么n!中含有2因子的个数等于(n-它的二进制形式中1的个数)

前置知识:

N!质因数2的个数 = [N / 2] + [N / 4] + [N / 8] + ....

因为N/2 表示的是2 4 6 8 10 12……这些相隔为2的数的个数;N / 4表示的是4 8 12 16这些相隔为4的数……这样子加起来就是N!质因数2的个数

定理:n!中含有2因子的个数等于(n-它的二进制形式中1的个数)证明

【有点树状数组 lowbit 的感觉】

补充:n&(n-1)作用:将n的二进制表示中的最低位为1的改为0,用于将n的二进制表示中的最低位为1的改为0

重点:a&b=b组合数是奇数,所以要找a&b=b的组合

设sum[b]的值是对于b来说,和b组合的组合数会变成奇数的个数;算法是动态规划

方程为dp[i]=a[i]+a[(所有i的二进制的子集)],例如 dp[110]=a[110]+a[100]+a[010]【a数组表示这个数有几个】

状态转移方程可表现为

如何实现?枚举1的最高位,从1枚举到第17位(2^17=131072)

3301. OIOIOI 

Problem #3301 - ECNU Online Judge

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1032304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣669 补9.16

最近大三上四天有早八&#xff0c;真的是受不了了啊&#xff0c;欧嗨呦&#xff0c;早上困如狗&#xff0c;然后&#xff0c;下午困如狗&#xff0c;然后晚上困如狗&#xff0c;尤其我最近在晚上7点到10点这个时间段看力扣&#xff0c;看得我昏昏欲睡&#xff0c;不自觉就睡了1…

关于Python安装Scrapy库的常见报错解决

目录 1、关于pip3命令的报错 2、执行scrapy报错&#xff08;Python3下的OpenSSL模块出错&#xff09; 3、卸载pyopenssl时报错 由于Scrapy该库在Windows下会存在兼容问题&#xff0c;下面介绍的是在Linux系统进行安装。 1、关于pip3命令的报错 报错代码&#xff1a; error…

redis漏洞修复:(CNVD-2019-21763)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、漏洞内容二、镜像准备1.确认镜像版本2.下载镜像 三、配置文件准备1.获取配置文件2.修改配置文件 四、启动redis容器五、修改iptables文件总结 前言 漏扫发…

Java JVM分析利器JProfiler 结合IDEA使用详细教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、JProfiler是什么&#xff1f;二、我的环境三、安装步骤1.Idea安装JProfiler插件1.下载程序的安装包 四、启动 前言 对于我们Java程序员而言&#xff0c;肯…

博通强迫三星签不平等长约,被韩处罚1亿元 | 百能云芯

近日&#xff0c;博通&#xff08;Broadcom&#xff09;这家国际知名的半导体公司因其市场主导地位的滥用&#xff0c;遭到了韩国公平贸易委员会&#xff08;FTC&#xff09;的严厉制裁&#xff0c;罚款高达191亿韩元&#xff0c;约合人民币1.04亿元。这一惩罚背后的故事揭示了…

用AVR128单片机的音乐门铃

一、系统方案 1、使用按键控制蜂鸣器模拟发出“叮咚”的门铃声。 2、“叮”声对应声音频率714Hz&#xff0c;“咚”对应声音频率500Hz,这两种频率由ATmega128的定时器生成&#xff0c;定时器使用的工作模式自定&#xff0c;处理器使用内部4M时钟。“叮”声持续时间300ms&#x…

Linux新手教程||Linux vi/vim

所有的 Unix Like 系统都会内建 vi 文书编辑器&#xff0c;其他的文书编辑器则不一定会存在。 但是目前我们使用比较多的是 vim 编辑器。 vim 具有程序编辑的能力&#xff0c;可以主动的以字体颜色辨别语法的正确性&#xff0c;方便程序设计。 什么是 vim&#xff1f; Vim是…

链表oj3(Leetcode)——相交链表;环形链表

一&#xff0c;相交链表 相交链表&#xff08;Leetcode&#xff09; 1.1分析 看到这个我们首先想到的就是一个一个比较他们的值有相等的就是交点&#xff0c;但是如果a1和b2的值就相等呢&#xff1f;所以这个思路不行&#xff0c;第二种就是依次比较链表&#xff0c;但是这…

xdebug3开启profile和trace

【xdebug开启profiler】 https://xdebug.org/docs/profiler http://www.xdebug.org.cn/docs/profiler 1、php.ini添加下面配置然后重启php容器&#xff1a; xdebug.modeprofile ;这个目录保存profile和trace文件 xdebug.output_dir /var/tmp/xdebugPHP日志提示报错&#xff1a…

ultraEdit正则匹配多行(xml用)

在ultraEdit中&#xff0c;我想选取<channel到</channel>之间的多行&#xff08;进行删除&#xff09;。在perl模式下&#xff0c;命令为“<channel[\s\S]?</channel>”。下面是xml文件&#xff1a; <!--This XML file does not appear to have any sty…

版本控制系统git:一文了解git,以及它在生活中的应用,网站维护git代码,图导,自动化部署代码

目录 1.Git是什么 2.git在生活中的应用 2.1git自动化部署代码 3.网站维护git代码 3.1如何在Git代码托管平台等上创建一个仓库 3.2相关文章 4.ruby实现基础git 4.1.Git add 4.2 Git commit 4.3 Git log 1.Git是什么 Git是一个版本控制系统&#xff0c;它可以追踪文件的…

C++11的一些新特性|线程库|包装器|lambda表达式

文章目录 目录 文章目录 一、可变参数模板 1.可变参数模板 2.STL容器中emplace相关函数接口: 二、lambda表达式 1.c98中的一个例子 2.lambda表达式 三、包装器 1.fuction包装器 四、线程库 1.thread类简单介绍 2.并发和并行的区别 3.线程函数参数 4.原子性操作库…

关于Java多线程的那些事

多线程 多线程1. 关于多线程的理解1.1 进程和线程1.2 并行和并发1.3 线程调度 2. 创建多线程的方式创建线程有哪几种方式&#xff1f;2.1 通过继承Thread类来创建并启动线程的步骤如下&#xff1a;2.2 通过实现Runnable接口来创建并启动线程的步骤如下&#xff1a;2.3 通过实现…

充电江湖暗战,充电运营商为什么忌惮平台崛起?

配图来自Canva可画 话说&#xff1a;“天下大势&#xff0c;分久必合&#xff0c;合久必分”&#xff0c;日前充电运营商与第三方充电平台再次倾情上演分手戏码。 8月中旬&#xff0c;新能源汽车充电运营服务商特来电、星星充电与云快充&#xff0c;毫无预兆的下架第三方充电…

Qt创建线程(线程池)

1.线程池可以创建线程统一的管理线程&#xff08;统一创建、释放线程&#xff09; 2.使用线程池方法实现点击开始按钮生成10000个随机数&#xff0c;然后分别使用冒泡排序和快速排序排序这10000个随机数&#xff0c;最后在窗口显示排序后的数字&#xff1a; mainwindow.h文件…

CATIA | 如何创建曲面点云数据并保存

问题描述 CATIA中创建的曲面模型&#xff0c;如何转换成可读的点云数据&#xff08;点云坐标数据&#xff09;输出保存&#xff1b; 操作流程&#xff08;示例&#xff09; 1、打开软件 2、创建三维曲面模型 &#xff08;具体步骤省略&#xff09; 3、文件另存为“.stl” …

wallet connect简单使用

wallet connect简单使用 准备工作安装配置打包测试 准备工作 新建一个文件夹xxx 右键在终端中打开 npm init -y在文件夹中新建src目录 在src目录中新建index.html和index.js文件 目录大概就这样我这是打包过的 安装 按照官方文档先安装 官方页面长这样 我们需要用到的是we…

基于Dijkstra、A*和动态规划的移动机器人路径规划(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑…

【软件设计师-中级——刷题记录3(纯干货)】

目录 数据交换模式知识产权与标准化之侵权判断OSI专业模型&#xff1a;7-克制自己的同理心8-不要为不值得的人和事浪费时间9-做个长期主义者 每日一言&#xff1a;持续更新中... 个人昵称&#xff1a;lxw-pro 个人主页&#xff1a;欢迎关注 我的主页 个人感悟&#xff1a; “失…

20 mysql const 查询

前言 这里主要是 探究一下 explain $sql 中各个 type 诸如 const, ref, range, index, all 的查询的影响, 以及一个初步的效率的判断 这里会调试源码来看一下 各个类型的查询 需要 lookUp 的记录 以及 相关的差异 测试表结构信息如下 CREATE TABLE tz_test (id int(1…