活动地址:CSDN21天学习挑战赛
假设p[1]为0,那么就可以通过q数组来求出每个p[i]相对于p[1]的增量了,然后找出最小的值这个值就是1,让这个数变为1的代价是x,每个数都加上x原排列就出来了
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=9999973;
- const int N = 2e5+5;
- ll n,a[200005],vis[400005];
- int main(){
- scanf("%lld",&n);
- a[1]=0;
- ll minn=0;
- for(int i=2;i<=n;i++) scanf("%lld",&a[i]),a[i]+=a[i-1],minn=min(minn,a[i]);
- for(int i=1;i<=n;i++) a[i]=a[i]+(1LL-minn);
- ll m=0;
- for(int i=1;i<=n;i++){
- if(a[i]>n||a[i]<=0){m=-10;break;}
- if(!vis[a[i]]) vis[a[i]]=1,m++;
- }
- if(m==n){
- for(int i=1;i<=n;i++) printf("%lld ",a[i]);
- }
- else printf("-1");
- system("pause");
- return 0;
- }
先说状压dp的做法,f[i][j]表示已经求出了c[i],前i个c[i]是否可以或成j,我们枚举a,枚举b,枚举当前状态也就是前i个c[i]或的和,然后枚举a[i]&b[j]的子集也就是c[i]的子集,看看是否可以转移到f[i][j],由于是枚举子集,所以时间复杂度会减少很多
C. Boboniu and Bit Operations(状压)_issue是fw的博客-CSDN博客
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=9999973;
- const int N = 2e5+5;
- ll n,m,a[205],b[205],f[205][1<<10];
- int main(){
- scanf("%lld%lld",&n,&m);
- for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
- for(int j=1;j<=m;j++) scanf("%lld",&b[j]);
- f[0][0]=1;
- for(int i=1;i<=n;i++)
- for(int j=1;j<=m;j++){
- ll v=a[i]&b[j];
- for(int u=0;u<(1<<9);u++){
- if((u&v)!=v) continue;//v中的1 u应该都有
- for(int w=v;w;w=(w-1)&v)
- f[i][u]|=f[i-1][u^w];
- f[i][u]|=f[i-1][u];
- }
- }
- for(int i=0;;i++)
- if(f[n][i]){printf("%d ",i);break;}
- system("pause");
- return 0;
- }
思维的做法:因为或运算只会让数不变或增加,所以最优的状态应该是不变,所以我们应该让c[i]都相同且尽量最小,如果c[i]不相同的话或出来的值会大于所有的c[i]这显然是不行的,所以最小值应该会出现在所有c[i]都相同的情况下,那我们就枚举测试每个答案行不行就可以了,因为数据范围小也用不到二分
C. Boboniu and Bit Operations(位运算操作+思维)Codeforces Round #664 (Div. 2)_unique_pursuit的博客-CSDN博客
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=9999973;
- const int N = 2e5+5;
- ll n,m,a[205],b[205];
- bool check(ll s){
- for(int i=1;i<=n;i++){
- ll flag=0;
- for(int j=1;j<=m;j++){
- ll tmp=a[i]&b[j];
- if((tmp|s)==s) flag=1;
- if(flag) break;
- }
- if(!flag) return false;
- }
- return true;
- }
- int main(){
- scanf("%lld%lld",&n,&m);
- for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
- for(int i=1;i<=m;i++) scanf("%lld",&b[i]);
- for(int i=0;i<(1<<10);i++){
- if(check(i)){printf("%d",i);break;}
- }
- system("pause");
- return 0;
- }
这个题也是用到了之前用到的前缀和技巧,但是比之前那个好像更难了点,对于一个前缀和来说,要求有多少区间的和是k的倍数,那我们可以对前缀和数组的每个数都取余k,之后每个数出现了x次就有C(x,2)个区间是k的倍数,0例外是C(x+1,2),因为1个0也满足条件;对于一个数组我们需要知道这个数组用到了0-k-1中的哪些数,这些数出现了多少次以及一共有多少区间符合条件,那我们就设一个dp[i][len][s]表示前i个数中用了len个,可以产生s个符合条件的区间,枚举到i,枚举想要用num个i,那么转移方程就是dp[i][len+num][s+C(num,2)]=dp[i-1][len][s],要是放0的话因为例外所以要是dp[i][len+num][s+C(num+1,2)],代码应该是因为方便转移将i都加了1位
2022牛客暑期多校训练营7 个人题解 更新至5题 - 知乎 (zhihu.com)
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=998244353;
- const int N = 2e5+5;
- ll fac[105];
- ll qpow(ll a,ll b){
- ll res=1;
- while(b){
- if(b&1) res=res*a%mod;
- a=a*a%mod;
- b>>=1;
- }
- return res;
- }
- ll getinv(ll a,ll mod){return qpow(a,mod-2);}
- ll C(ll x,ll y){//求组合数模板
- return fac[x]*getinv(fac[y],mod)%mod*getinv(fac[x-y],mod)%mod;
- }
-
- ll n,k,t,dp[70][70][3005],c[105][105];
- int main(){
- fac[0]=1;
- for(ll i=1;i<=70;i++) fac[i]=fac[i-1]*i%mod;
- for(ll i=0;i<=64;i++)
- for(ll j=i;j<=65;j++)
- c[j][i]=C(j,i);
- scanf("%lld%lld%lld",&n,&k,&t);
- dp[0][0][0]=1;
- for(int i=1;i<=k;i++)
- for(int len=0;len<=n;len++)
- for(int s=0;s<=t;s++)
- if(dp[i-1][len][s])
- for(int num=0;num+len<=n;num++){
- if(i==1) dp[i][num+len][c[num+1][2]+s]=(dp[i][num+len][c[num+1][2]+s]+dp[i-1][len][s]*c[num+len][num]%mod)%mod;
- else dp[i][num+len][c[num][2]+s]=(dp[i][num+len][c[num][2]+s]+dp[i-1][len][s]*c[num+len][num]%mod)%mod;
- }
- printf("%lld",dp[k][n][t]);
- system("pause");
- return 0;
- }
之前做过的一道题,当时的思想没有跟上现在来重新做一遍,这其实很像n皇后问题,之前是用搜索做,现在是用代码更简单的dp来做,用一个二进制数来表示一行的放置情况,预处理出所有合法情况之后枚举每行的情况,和上一行作比较如果可行就枚举已经放了多少个国王来进行转移
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=998244353;
- const int N = 2e5+5;
- ll n,m,dp[10][1<<9][105];
- ll ok[1<<9],cnt=0;
- int main(){
- scanf("%lld%lld",&n,&m);
- for(int i=0;i<1<
- if(i&i>>1||i&i<<1) continue;
- ok[++cnt]=i;
- }
- dp[0][1][0]=1;
- for(int i=1;i<=n;i++)
- for(int j=1;j<=cnt;j++)
- for(int k=1;k<=cnt;k++){
- if(ok[j]&ok[k]||ok[j]>>1&ok[k]||ok[j]<<1&ok[k]) continue;
- ll x=__builtin_popcount(ok[j]),y=__builtin_popcount(ok[k]);
- for(int l=y;l<=m-x;l++) dp[i][j][l+x]+=dp[i-1][k][l];
- //第k种情况已经有y个了,前i-2行可能也有放置的国王,所以从y开始枚举
- }
- ll ans=0;
- for(int i=1;i<=cnt;i++) ans+=dp[n][i][m];
- printf("%lld\n", ans);
- system("pause");
- return 0;
- }
P1879 [USACO06NOV]Corn Fields G - 洛谷 | 状压dp
这题和上一题是差不多的思路,这一个还少了个条件不需要考虑放的个数了,但是需要判断是不是和初始的情况一致,这个一个for循环就可以判断了
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=100000000;
- const int N = 2e5+5;
- ll n,m,dp[20][1<<12],a[20][20];
- ll ok[1<<12],cnt=0;
- int main(){
- scanf("%lld%lld",&m,&n);
- for(int i=1;i<=m;i++)
- for(int j=1;j<=n;j++)
- scanf("%lld",&a[i][j]);
- for(int i=0;i<1<
- if(i&i>>1||i&i<<1) continue;
- ok[++cnt]=i;
- }
- dp[0][1]=1;
- for(int i=1;i<=m;i++)
- for(int j=1;j<=cnt;j++)
- for(int k=1;k<=cnt;k++){
- if(ok[j]&ok[k]) continue;
- ll flag=1;
- for(int l=1;l<=n;l++)
- if(ok[j]>>l-1&1!=a[i][n-l+1]){flag=0;break;}
- if(flag) dp[i][j]=(dp[i][j]+dp[i-1][k])%mod;
- }
- ll ans=0;
- for(int i=1;i<=cnt;i++) ans=(ans+dp[m][i])%mod;
- printf("%lld\n", ans);
- system("pause");
- return 0;
- }
P2148 [SDOI2009] E&D - 洛谷 | sg函数
看了一下sg函数,套路就是根据题意打表然后找规律,求出sg函数之后判断异或和
这是打的表
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=100000000;
- const int N = 2e5+5;
- ll t,n;
- // ll sg(ll a,ll b){
- // if(a%2&&b%2) return 0;
- // if(a%2&&b%2==0) return sg(b,a);
- // if(a%2==0&&b%2) return sg(a,b+1);
- // if(a%2==0&&b%2==0) return sg(a/2,b/2)+1;
- // }
- // ll db(ll a,ll b){
- // if(dbsg[a][b]) return dbsg[a][b];
- // vector
v; - // for(ll i=1;i<=a;i++) v.push_back(db(i,a-i));
- // for(ll i=1;i<=b;i++) v.push_back(db(i,b-i));
- // sort(v.begin(),v.end());
- // ll x=0;
- // for(int i=0;i
- // if(x!=v[i]) return dbsg[a][b]=x;
- // x=v[i]+1;
- // }
- // return x;
- // }
- std::map
, ll> sg; -
- int calc(pair
c) { - if (sg.count(c)) return sg[c];
- std::vector<int> s;
- for(int i=1;i<=c.first-1;i++) s.push_back(calc({i, c.first - i}));
- for(int i=1;i<=c.second-1;i++) s.push_back(calc({i, c.second - i}));
- std::sort(s.begin(), s.end());
- s.erase(std::unique(s.begin(), s.end()), s.end());
- ll lst = -1;
- for (auto i : s) {
- if (i != lst + 1) return sg[c] = lst + 1;
- lst = i;
- }
- return sg[c] = lst + 1;
- }
- int main(){
- for(int i=1;i<=20;i++){
- for(int j=1;j<=20;j++)
- cout<<calc({i,j})<<"\t";
- cout<<"\n";
- }
- system("pause");
- return 0;
- }
这是ac代码
- #include
- using namespace std;
- #define ll long long
- #define lowbit(i) ((i)&(-i))
- const ll mod=100000000;
- const int N = 2e5+5;
- ll t,n;
- ll sg(ll a,ll b){
- if(a%2&&b%2) return 0;
- if(a%2&&b%2==0) return sg(b,a);
- if(a%2==0&&b%2) return sg(a,b+1);
- if(a%2==0&&b%2==0) return sg(a/2,b/2)+1;
- }
- int main(){
- scanf("%lld",&t);
- while(t--){
- ll res=0;
- scanf("%lld",&n);
- for(int i=2;i<=n;i+=2){
- ll a,b;
- scanf("%lld%lld",&a,&b);
- res^=sg(a,b);
- }
- if(res) printf("YES\n");
- else printf("NO\n");
- }
- system("pause");
- return 0;
- }
-
相关阅读:
Windows 的 PowerShell 提供了哪些命令
FSC认证助您进入日新月异的时尚领域
2023计算机毕业设计SSM最新选题之java企业员工信息管理系统677du
猿创征文|python求解四位数 青少年编程电子学会python编程等级考试三级真题解析2021年03月
Linux基本使用
虹科方案 | 汽车CAN/LIN总线数据采集解决方案
C语言条件运算符——三元表达式例题(素材来自C技能树)
删除不成功的免密登录重新做免密
SpringMVC工作原理
堆栈的类型及特点
-
原文地址:https://blog.csdn.net/weixin_52621204/article/details/126247216