A - A+...+B Problem 常识
Problem Statement
Snuke has N integers. Among them, the smallest is A, and the largest is B. We are interested in the sum of those N integers. How many different possible sums there are?
用\(n\)个在\([A,B]\)之间的数加和能够组合出来的不同的数的个数.
Constraints
- \(1 \leq N,A,B \leq 10^9\)
- A and B are integers.
Solution
常识
#include <cstdio>
#include <cstring>
typedef long long ll;
int main(){
int n,a,b;scanf("%d%d%d",&n,&a,&b);
ll l = 1LL*a*(n-1) + b,r = 1LL*b*(n-1) + a;
if(l > r) {puts("0");return 0;}
else printf("%lld\n",(r-l+1));
return 0;
}
B - Evilator 贪心
Problem Statement
Skenu constructed a building that has N floors. The building has an elevator that stops at every floor.
There are buttons to control the elevator, but Skenu thoughtlessly installed only one button on each floor - up or down. This means that, from each floor, one can only go in one direction. If Si is U, only "up" button is installed on the i-th floor and one can only go up; if Si is D, only "down" button is installed on the i-th floor and one can only go down.
The residents have no choice but to go to their destination floors via other floors if necessary. Find the sum of the following numbers over all ordered pairs of two floors (i,j): the minimum number of times one needs to take the elevator to get to the j-th floor from the i-th floor.
一个\(n\)层高的楼有一个电梯,在每一层时只有'U'和'D'两种按钮中的一个按钮.
'U'按钮可以到达任意一个大于当前楼层的层数.'D'按钮类似.
设\(dis(u,v)\)表示从\(u\)层到\(v\)层按按钮的最小次数
求\(\sum_{i=1}^n\sum_{j=1}^ndis(i,j)\)
Constraints
- \(2 \leq |S| \leq 105\)
- \(S_i\) is either U or D.
- \(S_1\) is U.
- \(S_{|S|}\) is D.
Solution
可以考虑枚举每一层,然后计算出该层到所有层的距离.
对于某一层,如果按钮为'D',那么到以下的所有楼层只需要按一次按钮.
到上面的楼层的话,只要向下找到一个向上的按钮就好了,所以只需要按两次按钮.
统计每一层,求和即为答案.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 100010;
char s[maxn];
int main(){
scanf("%s",s+1);
int n = strlen(s+1);
ll ans = 0;
rep(i,1,n){
if(s[i] == 'U') ans += (n-i) + ((i-1)<<1);
else ans += (i-1) + ((n-i)<<1);
}printf("%lld\n",ans);
return 0;
}
C - Nuske vs Phantom Thnook 思维题
Problem Statement
Nuske has a grid with N rows and M columns of squares. The rows are numbered 1 through N from top to bottom, and the columns are numbered 1 through M from left to right. Each square in the grid is painted in either blue or white. If Si,j is 1, the square at the i-th row and j-th column is blue; if Si,j is 0, the square is white. For every pair of two blue square a and b, there is at most one path that starts from a, repeatedly proceeds to an adjacent (side by side) blue square and finally reaches b, without traversing the same square more than once.
Phantom Thnook, Nuske's eternal rival, gives Q queries to Nuske. The i-th query consists of four integers xi,1, yi,1, xi,2 and yi,2 and asks him the following: when the rectangular region of the grid bounded by (and including) the xi,1-th row, xi,2-th row, yi,1-th column and yi,2-th column is cut out, how many connected components consisting of blue squares there are in the region?
Process all the queries.
给定一个\(n*m\)的网格图。每个格子有\(0/1\)两种颜色,保证任意两个\(1\)的格子最多有一条路径连接它们.
每次询问一个矩形区域内\(1\)格子的联通块的数目。
题目中所有联通都是四联块
Constraints
- \(1 \leq N,M \leq 2000\)
- \(1 \leq Q \leq 200000\)$
- \(S_{i,j}\) is either 0 or 1.
- \(S_{i,j}\) satisfies the condition explained in the statement.
- \(1 \leq xi,1 \leq xi,2 \leq N(1 \leq i \leq Q)\)
- \(1 \leq yi,1 \leq yi,2 \leq M(1 \leq i \leq Q)\)
Solution
这道题乍一看我还以为是 [APIO 2017] 的题.
不过两道题有所不同,APIO的题诗保证总体的块都是连通的。
而这道题保证的是所有的块的联通方式组成了一片森林。
而对于森林来说,联通块的个数即点数减去边数.
所以统计一下格子内\(1\)的块数和边数即可.
利用二维前缀和可以做到\(O(n^2)\)预处理\(O(1)\)实现查询.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 2048;
const int maxm = 100010;
char s[maxn][maxn];
int sum_node[maxn][maxn];
int sum_edgex[maxn][maxn];
int sum_edgey[maxn][maxn];
int n,m;
inline void init(){
rep(i,1,n) rep(j,1,m){
if(s[i][j] == '1') sum_node[i][j] = 1;
else sum_node[i][j] = 0;
sum_node[i][j] += sum_node[i-1][j] + sum_node[i][j-1] - sum_node[i-1][j-1];
}
rep(i,1,n) rep(j,1,m){
if(s[i][j] == '1' && s[i-1][j] == '1') sum_edgex[i][j] = 1;
else sum_edgex[i][j] = 0;
if(s[i][j] == '1' && s[i][j-1] == '1') sum_edgey[i][j] = 1;
else sum_edgey[i][j] = 0;
sum_edgex[i][j] += sum_edgex[i-1][j] + sum_edgex[i][j-1] - sum_edgex[i-1][j-1];
sum_edgey[i][j] += sum_edgey[i-1][j] + sum_edgey[i][j-1] - sum_edgey[i-1][j-1];
}
}
inline int calc(int x2,int y2,int x1,int y1,int s[2048][2048]){
return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}
int main(){
read(n);read(m);int q;read(q);
rep(i,1,n) scanf("%s",s[i]+1);
init();
int x1,x2,y1,y2;
while(q--){
read(x1);read(y1);read(x2);read(y2);
if(x1 > x2) swap(x1,x2);
if(y1 > y1) swap(y1,y2);
int ans = 0;
ans -= calc(x2,y2,x1,y1+1,sum_edgey);
ans -= calc(x2,y2,x1+1,y1,sum_edgex);
ans += calc(x2,y2,x1,y1,sum_node);
printf("%d\n",ans);
}
return 0;
}
D - A or...or B Problem
Problem Statement
Nukes has an integer that can be represented as the bitwise OR of one or more integers between A and B (inclusive). How many possible candidates of the value of Nukes's integer there are?
用\(n\)个在\([A,B]\)之间的数 位或 能够组合出来的不同的数的个数.
Constraints
- \(1\leq A\leq B<260\)
- \(A\) and \(B\) are integers.
Solution
既然这道题要求的是位或后的到的值。
那么我们就将\(A,B\)转化成二进制去思考。
首先从高到低找到一个\(A,B\)不相同的二进制位,设为第\(r\)位
那么所有大于该二进制位的位都是没有用处的.可以直接删除.
这时我们把所有的数分成两个集合:
\(X : [A,2^r), Y : [2^r,B]\)
如果设\(k\)为\(B\)中第一个小于\(r\)的为\(1\)的二进制位.那么有:
- 只用\(X\)集合的数可以组合出 : \([A,2^r)\)
- 只用\(Y\)集合的数可以组合出 : \([2^r,2^r+2^{k+1}-1]\)
- 必须同时使用\(X,Y\)集合的数可以组合出 : \([2^r+A,2^{r+1}-1]\)
所以直接加上\([A,2^{r+1}-1]\)再判一下\((2^r+2^{k+1}-1)和(2^r+A)\)的关系减一下就好了.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(ll &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
int main(){
ll A,B;read(A);read(B);
if(A == B){
puts("1");
return 0;
}
ll r,k;
for(r = 60;(A & (1LL << r)) == (B & ( 1LL << r));-- r);
A &= (1LL << (r+1)) - 1;B &= (1LL << (r+1)) - 1;
ll ans = (1LL << r+1) - A;
for(k = r-1;k >= 0 && (B & (1LL << k)) == 0;-- k);
if(A > (1LL << k+1)) ans -= A - (1LL << k+1);
printf("%lld\n",ans);
return 0;
}
E - Mr.Aoki Incubator
Problem Statement
Takahashi is an expert of Clone Jutsu, a secret art that creates copies of his body.
On a number line, there are N copies of Takahashi, numbered 1 through N. The i-th copy is located at position Xi and starts walking with velocity Vi in the positive direction at time 0.
Kenus is a master of Transformation Jutsu, and not only can he change into a person other than himself, but he can also transform another person into someone else.
Kenus can select some of the copies of Takahashi at time 0, and transform them into copies of Aoki, another Ninja. The walking velocity of a copy does not change when it transforms. From then on, whenever a copy of Takahashi and a copy of Aoki are at the same coordinate, that copy of Takahashi transforms into a copy of Aoki.
Among the 2N ways to transform some copies of Takahashi into copies of Aoki at time 0, in how many ways will all the copies of Takahashi become copies of Aoki after a sufficiently long time? Find the count modulo 109+7.
给定一个数轴上的\(n\)各点及他们的速度,定义红色点和黑色点在某一刻相遇时黑色点会变成红色.
问有多少种初始红点的选取方案使得无限长的时间后所有点都是红色.
Constraints
- \(1\leq N\leq 200000\)
- \(1\leq Xi,Vi\leq 109(1\leq i\leq N)\)
- \(X_i\) and \(V_i\) are integers.
- All \(X_i\) are distinct.
- All \(V_i\) are distinct.
Solution:
这道题好神啊.
首先考虑两点相遇的条件,设为\(i,j\).
则有:
- \(x_i < x_j \text{且} v_i > v_j\)
- \(x_i > x_j \text{且} v_i < v_j\)
那么假设说我们将所有的点按照他们的速度排序.
然后设\(L_i\)表示点\(i\)左侧最靠左\(x\)值大于\(x_i\)的点.
相应的设\(R_i\)表示点\(i\)右侧最靠右的\(x\)值小于\(x_i\)的点.
容易发现如果选取了第\(i\)个点,那么\([L_i,R_i]\)中的点都会被染色.
然后就可以将问题转化为选取若干组区间覆盖整个区间.
容易发现所有区间都不会存在包含关系.
所以可以按照拓扑图进行\(dp\).
\(S\)连向所有左端点为\(1\),所有右端点为\(n\)的点连向\(T\).
两个区间\(i,j\space (i<j)\)若\(L_j \leq R_i+1\)则连一条\(i \to j\)的边.
连出来的图一定是\(DAG\),所以在\(DAG\)上跑路径计数即可.
但是这个\(DAG\)明显不能直接建出来,因为边数太大了.
但是我们可以直接在这个序列上跑\(dp\),用线段树优化\(dp\)即可.
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
x=0;char ch;bool flag = false;
while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 200010;
const int mod = 1e9+7;
int Tmx[maxn<<2],Tmn[maxn<<2],T[maxn<<2];
void modify(int rt,int l,int r,int pos,int val){
if(l == r){
Tmx[rt] = Tmn[rt] = val;
T[rt] += val;
if(T[rt] >= mod) T[rt] -= mod;
return ;
}
int mid = (l+r) >> 1;
if(pos <= mid) modify(rt<<1,l,mid,pos,val);
else modify(rt<<1|1,mid+1,r,pos,val);
Tmx[rt] = max(Tmx[rt<<1],Tmx[rt<<1|1]);
Tmn[rt] = min(Tmn[rt<<1],Tmn[rt<<1|1]);
T[rt] = T[rt<<1] + T[rt<<1|1];if(T[rt] >= mod) T[rt] -= mod;
}
int query_min(int rt,int l,int r,int L,int R){
if(L <= l && r <= R) return Tmn[rt];
int mid = (l+r) >> 1;
if(R <= mid) return query_min(rt<<1,l,mid,L,R);
if(L > mid) return query_min(rt<<1|1,mid+1,r,L,R);
return min(query_min(rt<<1,l,mid,L,R),query_min(rt<<1|1,mid+1,r,L,R));
}
int query_max(int rt,int l,int r,int L,int R){
if(L <= l && r <= R) return Tmx[rt];
int mid = (l+r) >> 1;
if(R <= mid) return query_max(rt<<1,l,mid,L,R);
if(L > mid) return query_max(rt<<1|1,mid+1,r,L,R);
return max(query_max(rt<<1,l,mid,L,R),query_max(rt<<1|1,mid+1,r,L,R));
}
int query_sum(int rt,int l,int r,int L,int R){
if(L <= l && r <= R) return T[rt];
int mid = (l+r) >> 1;
if(R <= mid) return query_sum(rt<<1,l,mid,L,R);
if(L > mid) return query_sum(rt<<1|1,mid+1,r,L,R);
return (query_sum(rt<<1,l,mid,L,R) + query_sum(rt<<1|1,mid+1,r,L,R)) % mod;
}
struct Node{
int x,v;
bool friend operator < (const Node &a,const Node &b){
return a.v < b.v;
}
}a[maxn];
struct seg{
int L,R;
bool friend operator < (const seg &a,const seg &b){
return a.R == b.R ? a.L < b.L : a.R < b.R;
}
}se[maxn];
int seq[maxn];
int main(){
int n;read(n);
rep(i,1,n){
read(a[i].x);read(a[i].v);
seq[i] = a[i].x;
}sort(seq+1,seq+n+1);sort(a+1,a+n+1);
rep(i,1,n){
a[i].x = lower_bound(seq+1,seq+n+1,a[i].x) - seq;
}
memset(Tmn,0x3f,sizeof Tmn);
rep(i,1,n){
if(a[i].x+1 <= n) se[i].L = query_min(1,1,n,a[i].x+1,n);
else se[i].L = i;
se[i].L = min(se[i].L,i);
modify(1,1,n,a[i].x,i);
}
memset(Tmx,0,sizeof Tmx);
per(i,n,1){
if(a[i].x-1 >= 1) se[i].R = query_max(1,1,n,1,a[i].x-1);
else se[i].R = i;
se[i].R = max(se[i].R,i);
modify(1,1,n,a[i].x,i);
}
sort(se+1,se+n+1);
memset(T,0,sizeof T);
int ans = 0;
per(i,n,1){
int res = 0;
if(se[i].R == n) res ++ ;
res += query_sum(1,1,n,1,min(se[i].R+1,n));
if(res >= mod) res -= mod;
modify(1,1,n,se[i].L,res);
if(se[i].L == 1) ans += res;
if(ans >= mod) ans -= mod;
}printf("%d\n",ans);
return 0;
}