18.10.16 队测

T1 :

求给定集合有多少个非空子集可以分割成两个集合,使得它们的和相等。

其中:\(n\leq20~,~a[i]\leq1e8\)

题目可以转化成:给每个数前添加一个系数\(p\in[-1,1]\),使得和为0的方案数。

由于\(n\)比较小,所以可以考虑爆搜。朴素的爆搜可以枚举每个数不选,第一个集合,第二个集合,复杂度\(O(3^n)\) 。

然后发现根据套路,可以想到折半搜索\((meet~ in~ the ~middle)\)

先爆搜前十个的状态和\(sum\),然后爆搜后十个进行匹配,我这里用的是map.

#pragma GCC optimize(3)
#include<bits/stdc++.h>
using std :: vector ;
using std :: map;
using std :: clock;
#define ull unsigned long long
#define max(a,b) (a<b?b:a)
#define min(a,b) (a<b?a:b)
#define swap(x,y) x^=y^=x^=y
#define isdigit(ch) (ch>='0'&&ch<='9')
#define abs(x) (x<0?-x:x)
#define pb push_back
const int mod = 1e9+7;
inline void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
inline void write(int x) {print(x);puts("");}
int n,a[30],sum1,sum2,sum[30],ans;
namespace task1 {
    int Map[2000000],sta;
    void dfs(int x) {
    if(x>n) {if(!sum1&&!Map[sta]) Map[sta]=1,ans++;return ;}

    if(sum[x]+sum1<0) return ;
    if(-sum[x]+sum1>0) return ;
    sum1+=a[x];dfs(x+1);sum1-=a[x];
    sum1-=a[x];dfs(x+1);sum1+=a[x];
    sta^=1<<(x-1);dfs(x+1);sta^=1<<(x-1);
    }
    void solve() {dfs(1);write(ans-1);}
}
namespace task2 {
    struct node {map<int,int > s;vector<int > v;int num;};
    map <int,node > mp;
    map <int,int > mp2[5005];
    int sum=0,ans=0,sta=0,Map[5050];
    void dfs1(int x) {
    if(x>10) {
        if(!mp[sum].s[sta]) mp[sum].s[sta]=1,mp[sum].v.pb(sta),mp[sum].num++;
        return ;
    }
    sum+=a[x];dfs1(x+1);sum-=a[x];
    sum-=a[x];dfs1(x+1),sum+=a[x];
    sta^=1<<(x-1);dfs1(x+1);sta^=1<<(x-1);
    }
    map<int,int > check[5005];
    void dfs(int x) {
    if(x>n) {
        if(check[sta][-sum]) return ;
        check[sta][-sum]=1;
        int num=mp[-sum].num;
        for(int i=0;i<num;i++)
        if(!mp2[sta][mp[-sum].v[i]]) mp2[sta][mp[-sum].v[i]]=1,ans++;
        return ;
    }
    sum+=a[x];dfs(x+1);sum-=a[x];
    sum-=a[x];dfs(x+1),sum+=a[x];
    sta^=1<<(x-10);dfs(x+1);sta^=1<<(x-10);
    }
    void solve() {
    dfs1(1);for(int i=0;i<=5000;i++) Map[i]=0;sum=sta=0;
    dfs(11);write(ans-1);
    }
}
int main() {
    // freopen("subsets.in","r",stdin);
    // freopen("subsets.out","w",stdout);
    read(n);for(int i=1;i<=n;i++) read(a[i]);
    for(int i=n;i;i--) sum[i]=sum[i+1]+a[i];
    if(n<=10) task1 :: solve();
    else task2 :: solve();//write(clock());
    return 0;
}

(emacs缩进有点问题...)

T2:

给定一个\((0,1,2,..,n-1)\)的排列\(p\),然后求一个\((0,1,2,..,n-1)\)的排列\(q\)的个数

其中,\(q\)要满足 : 对排列\(s=(0,1,2,3,...,n-1)\)进行\(n-1\)次交换,每次交换\(s[q[i]]\),\(s[q[i]+1]\) 最后要满足\(s=p\); 答案对\(1e9+7\)取模

由于每个位置只能\(swap\)一次,所以\(q\)的某些相邻位置的值的大小关系是可以确定的。

这个问题就是一个简单的\(dp\)问题,设\(f[i][j]\)位前\(i\)个数第\(i\)的大小排名位\(j\),然后前缀和优化即可。

代码挖坑。。

T3 :

给出一个数组\(a\) ,有\(k\)个背包,每个背包能放连续的一段数,然后任取一个数\(x\in[0,P-1]\),给出一个P,对于每个\(a[i]\),\(a[i]=(a[i]+x)\%P\),最小化背包存的东西的最大值。

其中:\(P\leq 1e4,n\leq1e4\)

sample input :

5 5 2
0 4 2 1 3

sample output :

5

hint : x=3,a={3,2,0,4,1},最大值为5.

首先很容易想到,先暴力枚举\(x\),然后二分答案,\(O(Pn log n)\).

然后(看了题解才知道)可以用随机化优化时间复杂度。

随找出一个\(x\),然后\(check\)一下最优解,如果不能更优,则\(continue\),否则二分。

时间复杂度\(O(nP+nlognlogP)\).

#pragma GCC optimize(3)
#include<cstdio>
#include<ctime>
#include<algorithm>
using std :: random_shuffle;
using std :: clock;
#define ull unsigned long long
#define max(a,b) (a<b?b:a)
#define min(a,b) (a<b?a:b)
#define swap(x,y) x^=y^=x^=y
#define isdigit(ch) (ch>='0'&&ch<='9')
#define abs(x) (x<0?-x:x)
#define pb push_back
const int mod = 1e9+7;
inline void read(int &x) {
    x=0;int f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
inline void write(int x) {print(x);puts("");}
int n,P,k,a[100050],ans=1e9,g[100040],b[100020];
int check(int x) {
    int res=0,num=1;
    for(int i=1;i<=n;i++)
    if(a[i]>x) return false;
    else if(res+a[i]>x) res=a[i],num++;
    else res+=a[i];
    return num<=k;
}
int main() {
    read(n),read(P),read(k);
    for(int i=1;i<=n;i++) read(b[i]),b[i]%=P;
    for(int i=1;i<=P;i++)
    g[i]=i%P;
    random_shuffle(g+1,g+P+1);
    for(int i=1;i<=P;i++) {
    for(int j=1;j<=n;j++) a[j]=(b[j]+g[i])%P;
    if(!check(ans)) continue;
    int l=0,r=ans,res=1e8;
    while(l<=r) {
        int mid=(l+r)>>1;//write(mid);
        if(check(mid)) res=mid,r=mid-1;
        else l=mid+1;
    }
    ans=min(ans,res);
    }write(ans);
    return 0;
}

原文地址:https://www.cnblogs.com/hbyer/p/9799685.html

时间: 2024-11-08 01:28:20

18.10.16 队测的相关文章

2018.10.15队测T2

题意 给出n个与坐标轴平行的线段,保证没有两条共线的线段具有公共点,没有重合的线段 找出最大的十字形并输出大小R,大小为R的十字形指的是以一个中心点向四周延伸出R单位长度形成的图形 1≤n≤100000,所有坐标的范围在-10^9~10^9中 暴力 把线段按长度排序,O(n^2)枚举+O2 原文地址:https://www.cnblogs.com/tangjingrong/p/9798668.html

2018.10.23队测

T1:我不知道这是什么鬼啊,学长们推出一个部分分的结论,我就直接写了,18分,结果题目还锅了,不费改. T2:这题也锅了,改完数据后据说别校全场切了,听到学长说的差分序列后就想出标算的做法了,可惜学长他们写的不一样. 链接:sequence T3:状压大模拟,太恶心了,留坑... 原文地址:https://www.cnblogs.com/lcxer/p/9845748.html

2019.10.30 队测(晚上)

T1: 题目链接:Click here Solution: 考虑把给定的地图建出图来,那么询问实际上就是询问图上两点所有路径中最大边权的最小值 询问是一个老问题了,把边按权升序排列,用kruskal重构树,答案即为树上两点lca的点权 考虑如何建图,我们用一个bfs来建图即可,每次扩展到一个被其他城市扩展过的点,就加入一条边 因为不知道有多少条边,我们用vector来存边,注意判断两点是否在一个连通块内,注意路径压缩(不能直接用fa[x]啊) Code: #include<bits/stdc++

2018.10.15队测T3

题意 在一个网格图上,每次删掉一条边(u,v),再询问能否从u到v,如果能,就输出"HAHA",并删掉给出的该情况对应的边,否则就输出"DAJIA",并删掉另一条边 网格图大小<=500 1.删掉一条边,就相当于把边两侧的块联通了 于是就想到了并查集 2.两个顶点删边后不连通的情况: 即块A和块B在删边前已经在同一联通块中 另一种情况: 即在边界处的边被删了,这时可以看成块A块B与编号为0的块联通了 正解: 用并查集维护每个块的联通性,如果两个块在删边前就已处

2018.10.15队测

T1:算是sb题吧,我几乎完全不记得折半搜索了,虽然考试中想到过类似的做法,但是时间过不去就没想了.测试后惊讶发现我居然写过这道题,一模一样,但是一点印象都没有.也是个教训,以后学过的东西还是得复习.折半搜索这思路还是蛮简单的. 链接:subsets T2:这题是真的sao,我tm被这题坑死了,看他的描述就想到一个逆序对的结论,然后疯狂的去判无解的情况,然后写了个没有道理的贪心水分,事实证明一点道理都没有,还让我没时间写T3的暴力. 链接:swap T3:这题好像暂时是出锅了,留个坑待填. 原文

2018.10.17队测T3

题意 一棵n个节点的树,q次询问,每次询问编号为到l~r的节点构成的联通块个数 发现一条边(u,v)如果在联通块内,则l≤u≤r且l≤v≤r, 这就是二维偏序问题 所以每次求出满足条件的边的个数num,ans即为r-l+1-num 原文地址:https://www.cnblogs.com/tangjingrong/p/9813590.html

10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用

10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用 扩展 iptables应用在一个网段 http://www.aminglinux.com/bbs/thread-177-1-1.html sant,dnat,masquerade http://www.aminglinux.com/bbs/thread-7255-1-1.html iptables限制syn速率 http://www.aminglinux.com/bbs/thre

10.15 iptables filter表小案例;10.16—10.18 iptables nat

扩展: 1. iptables应用在一个网段: http://www.aminglinux.com/bbs/thread-177-1-1.html 2. sant,dnat,masquerade: http://www.aminglinux.com/bbs/thread-7255-1-1.html 3. iptables限制syn速率: http://www.aminglinux.com/bbs/thread-985-1-1.html 10.15 iptables filter表小案例 ipta

10.15 iptables filter表案例 10.16/10.17/10.18 iptable

七周四次课 10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用 10.15 iptables filter表案例 10.16/10.17/10.18 iptables nat表应用 打开端口转发, 调整内核参数 增加一条规则 所添加的规则 B机器设置默认网关 设置公共DNS C设备与A通信,通过端口转换的形式,将原有iptables清空 上面为进来的包进行转换,下面为出去的包进行转换 原文地址:http://blog.51cto.