T1 kom (容斥

\((1s32M)\)

给出\(N\)个互不相同的正整数,统计共有多少对数,它们有公共的一个数字(不一定在同一位置上)

输入

第一行一个正整数 \(N (1 ≤ N ≤ 1 000 000)\).

接下来\(N\)行,每行一个正整数\([1, 10^{18}]\),

输出

一个数,表示满足条件的对数

input

3
4
20
44

output

1

input

4
32
51
123
282

output

4

 

 

 

暴力(\(18*n^2\))

枚举两个数逐位判断

 

容斥(\(1024*1024\))

发现我们只在乎有哪些数字出现过,就可以只存每个数中那些数码出现过,这样内存需要(\(1e6*10\)),发现被卡内存了,那我们可以用二进制转换一下,二进制数中第\(i\)位代表这个数中\(i\)出现过,这样每个数都可以转化成一个\(10\)位二进制数最大是\(1023\);

这下就可以用桶统计每种数有多少个(这里的种类是指出现的数码集合相同),但题目只要求一个数码相同,所以对于每个数码进行统计会重复,自然想到了容斥;

枚举一个状态\(i(i<1024)\),对于一类数\(j\)(\(i\subset j\)具体体现就是\(j|i=j\)),所有的\(j\)都包含\(i\)这个状态,假设有\(num\)个,对答案的贡献是\(num*(num-1)/2\),容斥系数就是二进制下\(i\)中\(1\)的数量;

 

枚举替代容斥(\(1024*1024\))

因为状态很少,我们可以省略容斥,可以直接枚举两个状态(\(i,j\)),如果有交集(\(i\&j!=0\)),\(ans+=num[i]*num[j]\);

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000005;
int n;
int a[2050];
int x;
ll ans;
inline int read()
{
int x=0,f=1;char st=getchar();
while(st<'0'||st>'9'){if(st=='-') f=-1;st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*f;
} int main()
{
freopen("kom.in","r",stdin);
freopen("kom.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
{
x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9') x|=(1<<(c-'0')),c=getchar();
a[x]++;
} for(int i=1;i<1024;i++)
{
int num=0,num1=0;
for(int j=1;j<1024;j++)if((j|i)==j) num+=a[j];
for(int j=0;j<10;j++) if((i>>j)&1) num1++;
if(num1&1)
{
ans+=(ll)num*(num-1)/2;
} else
{
ans-=(ll)num*(num-1)/2;
} } printf("%lld",ans); fclose(stdin);
fclose(stdout);
return 0;
}

T2 fun (记搜

\((1s128M)\)

给出一程序(函数),输出程序运行后的结果。程序如下:

C++代码:

int fun() {
int ret = 0;
for (int a = X1; a <= Y1; ++a)
for (int b = X2; b <= Y2; ++b)
...
for (int <N-th> = XN; <N-th> <= YN; ++<N-th>)
ret = (ret + 1) % 1000000007;
return ret;
}

\(<N-th>\)表示字母表中每\(N\)个字母,$Xi \(和\)Yi$表示小于等于\(100000\)的正整数,或者表示一个循环变量(前面的)。

比如:\(X3\)可以为 \(a\),$ b\(,或者一个正整数。每一行的\)Xi and Yi$至少有一个是数。

输入

第一行一个正整数 \(N (1 ≤ N ≤ 26)\).

接下来\(N\)行,每行两个用空格隔开的量 \(Xi\ and\ Yi\),如果两个都是数字,则\(Xi ≤ Yi\).

输出

一个数字,表示程序的输出结果

2
1 2
a 3

output

5

input

3
2 3
1 2
1 a

output

10

input

3
1 2
a 3
1 b

output

11

 

 

 

蛮恶心的一道题;

就是让你数循环次数;但是有复杂的嵌套结构;

对于每一对依赖关系连一条边,我们可以得到很多树(森林),(因为每一层最多连一条边),这样的话只有父节点的取值会影响当前节点的循环次数;

我们可以在此基础上记搜;

我们发现只有根节点的左右边界都是数;

对于一个节点,在当前取值下的循环次数应是在当前取值下子节点的循环次数的,因为子节点总是有先后顺序的;

那记什么呢?我们发现当前节点会对同一边界情况搜很多次,我们记录一个\(f[i][j]\)代表\(i\)号节点,其父节点的取值是\(j\)的循环次数,其中\(j\)是左边界还是右边界是由节点自身情况决定的;

假如j是左边界可以得到

\[f[i][j]=\sum_{o=j}^{r}\prod{dfs(k,o)}
\]

\(k\)是当前节点的一个子节点,\(dfs(k,o)\)是搜第\(k\)层,其父亲取值是\(o\)的情况;

这里为什么又是加呢,很显然每个节点不同取值之间是没有影响的;

这样子可以的\(80\);

我们还可以考虑一下继承,假设我们的取值都是从小到大枚举的,那我们算取值是\(i\)时,\(i-1\)的情况已经算过了;

如果\(i\)是左边界,那\(i-1\)的情况比\(i\)多算了取值为\(i-1\)的影响,将这部分减去就可以了

如果\(i\)是右边界,那\(i-1\)的情况比\(i\)少算了取值为\(i\)的影响,将这部分加上就可以了

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1000000007;
int n;
char ca[30],cb[30];
int a[30],b[30];
ll ans=1;
ll f[30][100005];
struct skr
{
int to,nxt;
}t[30];
int head[30],cnt;
inline int read()
{
int x=0,f=1;char st=getchar();
while(st<'0'||st>'9'){if(st=='-') f=-1;st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*f;
} inline void add(int x,int y)
{
t[++cnt].to=y;t[cnt].nxt=head[x];head[x]=cnt;
} inline ll dfs(int x,int l)
{
if(~f[x][l]) return f[x][l];
f[x][l]=0;
if(!a[x])
{
f[x][l]=0;
ll res=1;
if(l>b[x])return f[x][l]=0;
if(~f[x][l-1])
{
res=1;
for(int k=head[x];k;k=t[k].nxt)
{
res=(res*dfs(t[k].to,l-1))%mod;
}
f[x][l]=(f[x][l-1]-res+mod)%mod;
}
else
{
for(int i=l;i<=b[x];i++)
{
res=1;
for(int k=head[x];k;k=t[k].nxt)
{
res=(res*dfs(t[k].to,i))%mod;
}
f[x][l]=(f[x][l]+res)%mod;
}
} }
else
{
f[x][l]=0;
ll res=1;
if(a[x]>l)return f[x][l]=0;
if(~f[x][l-1])
{
res=1;
for(int k=head[x];k;k=t[k].nxt)
{
res=(res*dfs(t[k].to,l))%mod;
}
f[x][l]=(f[x][l-1]+res)%mod;
}
else
{
for(int i=a[x];i<=l;i++)
{
res=1;
for(int k=head[x];k;k=t[k].nxt)
{
res=(res*dfs(t[k].to,i))%mod;
}
f[x][l]=(f[x][l]+res)%mod;
}
} }
return f[x][l];
} int main()
{
freopen("fun.in","r",stdin);
freopen("fun.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
{
int x=0;
char c=getchar();
while((!('a'<=c&&c<='z'))&&(!('0'<=c&&c<='9'))) c=getchar();
if('a'<=c&&c<='z')
{
ca[i]=c;
add(c-'a'+1,i);
}
else
{
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
a[i]=x;
}
x=0;c=getchar();
while((!('a'<=c&&c<='z'))&&(!('0'<=c&&c<='9'))) c=getchar();
if('a'<=c&&c<='z')
{
cb[i]=c;
add(c-'a'+1,i);
}
else
{
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
b[i]=x;
}
}
memset(f,-1,sizeof f);
for(int i=1;i<=n;i++)
if(a[i]&&b[i])
{
f[i][0]=0;
for(int j=a[i];j<=b[i];j++)
{
ll res=1;
for(int k=head[i];k;k=t[k].nxt)
res=(res*dfs(t[k].to,j))%mod;
f[i][0]=(f[i][0]+res)%mod;
}
ans=(ans*f[i][0])%mod;
}
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}

T3 ras (权值线段树

\((1.5s128M)\)

给定\(N\)个订单,每个订单用两个数字表示,一个表示订单交货时间,一个表示产品制作需要的时间,一个订单完成后(送货时间不计),就可收到小费,如果比规定时间提前\(K\)个时间,则可获得\(K\),注意准时送到得\(0\),迟送到就得对应的负值。(也就是订单上的时间减提交的时间),开始生产的时间为\(0\),请合理安排订单的顺序,以获得最大的小费,输出小费值。(第一问)

另外,给定\(C\)个修改,每个修改,改变一个订单,对于每次修改输出改后所有订单能得的最大小费数。

输入

第一行两个正整数N and C,表示订单数,和订单修改的次数.

接下来N行,每行两个数(Li,Ti)分别表示订单提交时间和订单制作时间。

接下来C行,每行三个数(R,L,T),依次原订单的编号,提交时间,制作时间。

Constraints:
1 ≤ N, C ≤ 200 000,
0 ≤ Li, L ≤ 100 000,
1 ≤ Ti, T ≤ 100 000,
1 ≤ R ≤ N.

输出

第一行一个数,表示最开始能获得的最大小费。

接下来C行,每行一个数,表示修改这个订单后能获得的最大小费。

input

3 2
10 2
6 5
4 3
1 6 1
3 0 10

output

3
2
-11

input

4 2
3 2
0 3
4 3
4 1
3 0 4
1 4 5

output

-8
-13
-18

input

6 7
17 5
26 4
5 5
12 4
8 1
18 2
3 31 3
4 11 5
4 19 3
5 23 2
6 15 1
5 19 1
3 10 4

output

27
59
56
69
78
81
82
58

对于第一个例子,原始订单完成的顺序\((1, 3, 2)\). 第一个订单完成时 \(t = 2\),第三个\(t = 5\),第二个 \(t = 10\).所以第一个订单可获\(8\)个小费,第二个获\(-1\),第\(3\)个获\(-4\))所以总数为\(3\).

第一次改变后,订单完成顺序不变,得小费为 \(5, 0, and -3\),第二次改变后,最优顺序为\((1, 2, 3)\), 小费为 \(5\),\(0\),$ and$ \(-16\)

 

 

 

每一个订单对答案的贡献是“截止时间-实际制作时间”,将每一个实际制作时间拆开,发现每一个订单的制作时间的影响是时间乘它后面的订单数+自己;

那么答案可以写成

\[\sum{l-t*(n-i+1)}
\]

我们将截止时间单独统计,将制作时间由小到大排序,以这样的顺序制作是最优的,因为这样让等待时间降至最小;

 

 

但问题涉及到了修改订单,我们考虑当前状态与上一个状态的差值;

修改一个点其实相当于把原值删掉再加入一个新值;

其实删除与加入是完全相反的,我们只讨论删除;

由于涉及到大小顺序,我们可以维护一个权值线段树(权值树状数组)

维护当前节点出现的次数和出现这么多次的总和,线段树分别维护两个值得和;

当删除某个订单时,先将它的\(l\)删去;

再删除某个值\(t\)时,先将对应值出现次数\(-1\),对应值的总和\(-t\);

再考虑少减了多少,

首先是它自己没有了,它原本的贡献是\(t*(n-i+1)\)

我们区间查询出有多少个比\(t\)小,假设是\(g\)个,答案应当加上\(t*(n-g)\)

其次是对其他值的影响,现在\(n\)变成了\(n-1\),而比\(t\)大的部分排名也减少了\(1\),所以这部分没有变,但比\(t\)小的就会少减一次,所以答案应该加上比\(t\)少了的元素的和

添加相反

 

 

#include<bits/stdc++.h>
#define ll long long
#define ls p<<1
#define rs p<<1|1
#define l(p) a[p].l
#define r(p) a[p].r
using namespace std; const int N=200005; int n,zc[N],num[N],c;
ll tot;
struct book
{
int l,t;
}b[N];
struct ST
{
ll sum;
int dat;
int l,r;
}a[N<<1];
inline bool cmp(ll x,ll y)
{
return b[x].t<b[y].t;
} inline int read()
{
int x=0,f=1;char st=getchar();
while(st<'0'||st>'9'){if(st=='-') f=-1;st=getchar();}
while(st>='0'&&st<='9') x=x*10+st-'0',st=getchar();
return x*f;
} inline void update(int p)
{
a[p].dat=a[ls].dat+a[rs].dat;
a[p].sum=a[ls].sum+a[rs].sum;
} inline void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
if(l==r)
{
a[p].dat=num[l];
a[p].sum=num[l]*l;
return ;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
update(p);
}
inline void change(int p,int d,int x)
{
if(l(p)==r(p))
{
a[p].dat+=x;
a[p].sum+=(ll)x*l(p);
return ;
}
int mid=l(p)+r(p)>>1;
if(d<=mid) change(ls,d,x);
else change(rs,d,x);
update(p);
} inline ll ask(int p,int l,int r)
{
if(l(p)>=l&&r(p)<=r) return a[p].sum;
int mid=l(p)+r(p)>>1;
ll val=0;
if(l<=mid) val+=ask(ls,l,r);
if(r>mid) val+=ask(rs,l,r);
return val;
} inline int asknum(int p,int l,int r)
{
if(l(p)>=l&&r(p)<=r) return a[p].dat;
int mid=l(p)+r(p)>>1;
int val=0;
if(l<=mid) val+=asknum(ls,l,r);
if(r>mid) val+=asknum(rs,l,r);
return val;
}
int main()
{
freopen("ras.in","r",stdin);
freopen("ras.out","w",stdout);
n=read();c=read();
for(int i=1;i<=n;i++)
{
b[i].l=read();b[i].t=read();
tot+=b[i].l,zc[i]=b[i].t;
num[b[i].t]++;
}
sort(zc+1,zc+1+n);
build(1,1,100000);
for(int i=1;i<=n;i++)
tot-=(ll)zc[i]*(n-i+1);
printf("%lld\n",tot);
for(int i=1;i<=c;i++)
{
int r=read(),l=read(),t=read(); tot-=b[r].l;
change(1,b[r].t,-1);
tot+=ask(1,1,b[r].t-1);
int g=asknum(1,1,b[r].t-1);
tot+=(ll)(n-g)*b[r].t; b[r].l=l;b[r].t=t;
tot+=l; change(1,b[r].t,1);
tot-=ask(1,1,b[r].t-1);
g=asknum(1,1,b[r].t-1);
tot-=(ll)(n-g)*b[r].t; printf("%lld\n",tot);
} fclose(stdin);
fclose(stdout);
return 0;
}

Test1016的更多相关文章

  1. NOIp2018集训test-10-16 (bike day2)

    “毕姥爷:今天的题好简单啊,你们怎么考得这么烂啊,如果是noip你们就凉透了啊“ 今天的题难度应该是3.2.1递减的,但是我不知道哪根筋没搭对,平时我最多1h多就弃题了,今天硬生生写了2h20min的 ...

  2. 数据文件个数大于1024时ORACLE数据文件FILE_ID及RELATIVE_FNO的变化示例

    通过ROWID计算数据块的相关信息:  --详见: 数据文件头块保留大小.ROWID.数据文件最大大小等数据库限制的说明 根据small file tablespace的ROWID,计算出表空间.数据 ...

随机推荐

  1. kafka connector 使用总结以及自定义connector开发

    Kafaka connect 是一种用于在Kafka和其他系统之间可扩展的.可靠的流式传输数据的工具.它使得能够快速定义将大量数据集合移入和移出Kafka的连接器变得简单.Kafka Connect可 ...

  2. 缓冲&缓存&对象池概念的理解

    一).缓冲 作用:缓解程序上下层之间的性能差异. 1).当上层组件的性能优于下层组件时加入缓冲机制可以减少上层组件对下 层组件的等待时间. 2).上层组件不需要等待下层组件接收全部数据,即可返回操作, ...

  3. GitHub 标星 1.6w+,我发现了一个宝藏项目,作为编程新手有福了!

    大家好,我是 Rocky0429,一个最近老在 GitHub 上闲逛的蒟蒻... 特别惭愧的是,虽然我很早就知道 GitHub,但是学会逛 GitHub 的时间特别晚.当时一方面是因为菜,看着这种全是 ...

  4. 2019牛客暑期多校训练营(第二场)J.Subarray

    题意:给你一个n 表示有n段连续的1序列 现在问你 在总长度为0~1e9-1的范围内有多少个大于0的子段 思路:假设我们统计了当前的前缀和 我们显然可以用树状数组维护一下前缀和 这样我们可以nlogn ...

  5. Spring Cloud第三篇 | 搭建高可用Eureka注册中心

    ​ ​本文是Spring Cloud专栏的第三篇文章,了解前两篇文章内容有助于更好的理解后面文章: Spring Cloud第一篇 | Spring Cloud前言及其常用组件介绍概览 Spring ...

  6. 在ubuntu18.04中安装opencv_contrib-3.2.0采坑教程

    由于最近要在OpenCV3中使用SIFT和SURF特征提取,而自从OpenCV2升级到OpenCV3版本后,SIFT.SURF等这些算法都被移出opencv默认项目库,而被放到叫opencv_cont ...

  7. 大型情感剧集Selenium:8_selenium网页截图的四种方法

    有时候,有时候,你会相信一切有尽头-当你的代码走到了尽头,那么保留最后一刻的状态尤为重要,此时你该如何操作?记录日志-没有将浏览器当前的状态进行截图来的直观! 那么,selenium截取截屏,有哪些方 ...

  8. 【开发记录】Linux常用命令记录(一)

    记录CentOS下,常用的命令.有时候很难记得清楚,同时方便新来的同学查阅.(将不停的追加和完善) 1)查看CPU情况 cat /proc/cpuinfo |grep "model name ...

  9. Python基础之升级pip版本

    使用python -m pip install --upgrade pip命令即可. 查看是否更新成功: 使用pip3 list命令即可.

  10. 09-Python-Socket编程

    一.Python-Socket编程 1.1.弄懂HTTP.Socket.TCP这几个概念 什么是HTTP协议?浏览器的本质就是一个socket客户端遵循HTTP协议,HTTP协议的本质:通过\r\n分 ...