还没补完,慢慢补,能写多少是多少···
2016
题目描述
给出正整数 n 和 m,统计满足以下条件的正整数对 (a, b) 的数量:
- 1 ≤ \leq ≤ a ≤ \leq ≤ n, 1 ≤ \leq ≤ b ≤ \leq ≤ m
- a × \times × b 是 2016 的倍数。
思路
(a
×
\times
× b)% mod = (a%mod
×
\times
×b%mod)%mod
所以如果找到一个a%mod == 0,那么 (n-a)/mod 就是%mod = 0的数量
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int n,m;
while(cin>>n>>m){
ll ans = 0;
for(int i=1;i<=min(n,2016);++i){
for(int j=1;j<=min(m,2016);++j){
if(i*j%2016 == 0){
ans += 1LL*((n-i)/2016+1)*((m-j)/2016+1);
// 加一是因为加上本身
}
}
}
cout<<ans<<endl;
}
return 0;
}
有向无环图
题目描述
Bobo 有一个 n 个点,m 条边的有向无环图(即对于任意点 v,不存在从点 v 开始、点 v 结束的路径)。
为了方便,点用 1, 2,
…
\dots
…,n 编号。
设
c
o
u
n
t
(
x
,
y
)
\mathrm{count}(x, y)
count(x,y) 表示点 x 到点 y 不同的路径数量(规定
c
o
u
n
t
(
x
,
x
)
=
0
\mathrm{count}(x, x) = 0
count(x,x)=0),Bobo 想知道
∑
i
=
1
n
∑
j
=
1
n
c
o
u
n
t
(
i
,
j
)
⋅
a
i
⋅
b
j
\sum_{i = 1}^n\sum_{j = 1}^n \mathrm{count}(i, j) \cdot a_i \cdot b_j
∑i=1n∑j=1ncount(i,j)⋅ai⋅bj 除以
(
1
0
9
+
7
)
(10^9+7)
(109+7)的余数。
其中,
a
i
,
b
j
a_i, b_j
ai,bj是给定的数列。
思路
因为只需要知道到所有点的数量和,而不是到定点的数量和。所有利用
d
p
i
dp_i
dpi维护从
i
i
i点到
i
i
i能到达的所有点的
b
b
b数组之和,如果现在有点
x
x
x指向
i
i
i的边。那么状态转移的式子为:
d
p
x
+
=
d
p
i
dp_x += dp_i
dpx+=dpi
d
p
x
+
=
b
i
dp_x += b_i
dpx+=bi
然后统计答案就OK了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int mod = 1e9+7;
vector<int> g[maxn];
int a[maxn],b[maxn];
int in[maxn],dp[maxn];
void init(int n){
memset(in,0,sizeof(in));
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
for(int i=1;i<=n;++i)
g[i].clear();
}
void Print(int n){
cout<<"dp"<<endl;
for(int i=1;i<=n;++i)
cout<<i<<" "<<dp[i]<<endl;
cout<<endl;
}
int main()
{
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
int n,m,x,y;
while(cin>>n>>m)
{
init(n);
for(int i=1;i<=n;++i)
cin>>a[i]>>b[i];
for(int i=0;i<m;++i){
cin>>x>>y;
g[y].push_back(x);
in[x]++;
}
queue<int> que;
for(int i=1;i<=n;++i)
if(in[i] == 0)
que.push(i);
while(!que.empty()){
int now = que.front();
que.pop();
for(int i=0;i<g[now].size();++i){
int x = g[now][i];
if(x == now)
continue;
dp[x] = (1LL*dp[x] + dp[now])%mod;
dp[x] = (1LL*dp[x] + b[now])%mod;
in[x]--;
if(in[x] == 0)
que.push(x);
}
}
ll ans = 0;
for(int i=1;i<=n;++i)
ans = (ans + (1LL*a[i]*dp[i])%mod)%mod;
cout<<ans<<endl;
//Print(n);
}
return 0;
}
Parenthesis
题目描述
规定一类字符串是平衡的,这类字符串的规定如下:
- 空串S认为是平衡的
- 如果 S 1 S_1 S1是平衡的,那么形如 S = ( S 1 ) S=(S_1) S=(S1),S也是平衡的
- 如果 A , B A,B A,B是平衡的,那么形如 S = A B S=AB S=AB,S也是平衡的
现在给定一个平衡的字符串,有
q
q
q个操作,每一个操作有两个数字
a
i
,
b
i
a_i,b_i
ai,bi
将原字符串的第
a
i
a_i
ai与
b
i
b_i
bi交换,问交换后的字符串是否依然平衡
数据规模为
1
e
5
1e5
1e5
思路
首先根据题目容易推出:
- 如果交换的两个字符相同,不影响字符串的平衡性
- 如果交换的两个字符 ′ ) ′ ')' ′)′在前, ′ ( ′ '(' ′(′在后,也不影响字符串的平衡性
现在需要考虑的就是最后一种情况:如果需要交换的两个字符
′
(
′
'('
′(′在前,
′
)
′
')'
′)′在后
很容易想到一个思路:
统计字符串的前缀和,即有一个左括号和加一,一个右括号减一。
在上述说的情况下交换对于前缀和数组的影响就是对于交换坐标的左闭右开区间进行区间减法,出现非法的情况就是前缀和中出现负数。
这个时候已经可以直接套一个线段树的板子,但区间减法的线段树写起来有点麻烦,所以还可以再优化。
即直接套一个区间查询的板子,看区间内有没有小于2的数。
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
const int INF = 0x3f3f3f3f;
char str[maxn];
int a[maxn],b[maxn],pre[maxn];
int tree[maxn<<2];
void build(int l,int r,int k){
if(l > r) return ;
if(l == r){
tree[k] = pre[l];
return ;
}
int mid = (l+r)>>1;
build(l,mid,k<<1);
build(mid+1,r,k<<1|1);
tree[k] = min(tree[k<<1],tree[k<<1|1]);
}
int query(int l,int r,int ql,int qr,int k){
if(l>qr || r<ql || l>r)
return INF;
if(l>=ql && r<=qr)
return tree[k];
int mid = (l+r)>>1;
return min(query(l,mid,ql,qr,k<<1),query(mid+1,r,ql,qr,k<<1|1));
}
int main()
{
//freopen("in.txt","r",stdin);
ios::sync_with_stdio(false);
int n,q;
while(cin>>n>>q)
{
memset(pre,0,sizeof(pre));
memset(tree,0,sizeof(tree));
cin>>str+1;
for(int i=0;i<q;++i)
cin>>a[i]>>b[i];
for(int i=1;i<=n;++i){
if(str[i] == '(')
pre[i] = pre[i-1]+1;
else
pre[i] = pre[i-1]-1;
}
build(1,n,1);
for(int i=0;i<q;++i){
if(str[a[i]] == str[b[i]])
cout<<"Yes"<<endl;
else{
if(a[i] > b[i]) swap(a[i],b[i]);
if(str[a[i]] == ')' && str[b[i]] == '(')
cout<<"Yes"<<endl;
else{
int now = query(1,n,a[i],b[i]-1,1);
if(now < 2) cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
}
}
}
return 0;
}
三角形和矩形
题目描述
obo 有一个三角形和一个矩形,他想求他们交的面积。
具体地,三角形和矩形由 8 个整数
x
1
,
y
1
,
x
2
,
y
2
,
x
3
,
y
3
,
x
4
,
y
4
x_1, y_1, x_2, y_2, x_3, y_3, x_4, y_4
x1,y1,x2,y2,x3,y3,x4,y4描述。
表示三角形的顶点坐标是
(
x
1
,
y
1
)
,
(
x
1
,
y
2
)
,
(
x
2
,
y
1
)
(x_1, y_1), (x_1, y_2), (x_2, y_1)
(x1,y1),(x1,y2),(x2,y1)
矩形的顶点坐标是 ( x 3 , y 3 ) , ( x 3 , y 4 ) , ( x 4 , y 4 ) , ( x 4 , y 3 ) (x_3, y_3), (x_3, y_4), (x_4, y_4), (x_4, y_3) (x3,y3),(x3,y4),(x4,y4),(x4,y3)
思路
计算几何板子题,需要注意不能用cout,不然会WA···
代码
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-10;
const double PI=acos(-1.0);
struct Point{
double x,y;
Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;
Vector operator -(Point a,Point b){
return Vector(a.x-b.x,a.y-b.y);
}
Vector operator +(Point a,Point b){
return Vector(a.x+b.x,a.y+b.y);
}
Vector operator *(Vector a,double p){
return Vector(a.x*p,a.y*p);
}
Vector operator /(Vector a,double p){
return Vector(a.x/p,a.y/p);
}
bool operator <(const Point& a,const Point& b){
return a.x<b.x||(a.x==b.x&&a.y<b.y);//在有精度需求,比如使用lower_bound的时候,加上dcmp()
}
int dcmp(double x){
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
bool operator ==(const Point& a,const Point& b){
return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Cross(Vector a,Vector b){
return a.x*b.y-a.y*b.x;
}//叉积
Point GetlineIntersection(Point p,Vector v,Point q,Vector w){
Vector u=p-q;
double t=Cross(w,u)/Cross(v,w);
return p+v*t;
}
typedef vector<Point> Polygon;
//多边形的有向面积,逆时针为正
double PolygonArea(Point po[],int n) {
double area = 0.0;
for(int i = 1; i < n-1; i++) {
area += Cross(po[i]-po[0], po[i+1]-po[0]);
}
return area * 0.5;
}
double PolygonArea(Polygon po) {
int n=po.size();
double area = 0.0;
for(int i = 1; i < n-1; i++) {
area += Cross(po[i]-po[0], po[i+1]-po[0]);
}
return area * 0.5;
}
double cross(Point a,Point b,Point c)
{
return Cross(a-c,b-c);
}
double CPIA(Point a[], Point b[], int na, int nb){//传入两个三角形,求相交部分的凸包
Point p[20], tmp[20]; //复制点集与临时点集(P其实可以用B来做
int tn, sflag, eflag; //每轮相交凸包的点,叉乘符号
a[na] = a[0], b[nb] = b[0]; //末点用初点复制方便首末点连边
memcpy(p,b,sizeof(Point)*(nb + 1)); //把B复制到P
for(int i=0;i<na&&nb>2;i++){ //扫一次A
sflag=dcmp(cross(a[i+1],p[0],a[i])); //取A两点与B第一点求叉乘符号
for(int j=tn=0;j<nb;j++,sflag=eflag){ //扫一次B,更新TMP,TN是点数
if(sflag>=0)tmp[tn++]=p[j]; //叉乘为正就是B数组的那个点压入
eflag=dcmp(cross(a[i+1],p[j+1],a[i]));//求叉乘符号
if((sflag^eflag)==-2)tmp[tn++]=GetlineIntersection(a[i],a[i+1]-a[i],p[j],p[j+1]-p[j]);
//tmp[tn++]=intersection(a[i],a[i+1],p[j],p[j+1]);//求交点
}
memcpy(p, tmp, sizeof(Point) * tn); //把TMP复制到P
nb = tn, p[nb] = p[0];//TN即TMP点数记到NB
}//其实该是NP表示P数组个数,这里省了个变量就用NB表示,下面第二行做参数而已
if(nb < 3) return 0.0; //相交部分凸包不够三个点,面积就是0
return PolygonArea(p, nb); //求出相交凸包部分的面积
}
double SPIA(Point a[], Point b[], int na, int nb){//传入两个多边形的点
int i,j; //循环变量
Point t1[4],t2[4]; //其实T13与T23没用上
double res=0,num1,num2; //答案初始化,及叉乘符号
a[na]=t1[0]=a[0],b[nb]=t2[0]=b[0]; //初始化T1,T2和ANA,BNB
for(i=2;i<na;i++){ //扫一次第一个多边形全部点
t1[1]=a[i-1],t1[2]=a[i]; //每次在第一个多边形取两个点赋给T11,T12
num1=dcmp(cross(t1[1],t1[2],t1[0]));//求出叉乘符号
if(num1<0)swap(t1[1],t1[2]); //小于0则改变T11,T12可使叉乘符号变正,实即改变T1三个点的顺逆
for(j=2;j<nb;j++){ //扫一次第二个多边形全部点
t2[1]=b[j-1],t2[2]=b[j]; //然后再在第二个多边形取两个点赋给T21,T22
num2=dcmp(cross(t2[1],t2[2],t2[0]));//求出叉乘符号
if(num2<0)swap(t2[1],t2[2]);//小于0则改变T11,T12可使叉乘符号变正,实即改变T1三个点的顺逆
res+=CPIA(t1,t2,3,3)*num1*num2; //累加相交部分面积
}
}
res=fabs(res);
return res;
}
Point a[5],b[5];
int main()
{
//freopen("in.txt","r",stdin);
int x1,x2,x3,x4,y1,y2,y3,y4;
while(scanf("%d %d %d %d",&x1,&y1,&x2,&y2)!=EOF){
scanf("%d %d %d %d",&x3,&y3,&x4,&y4);
a[0].x = x1;a[0].y = y1;
a[1].x = x1;a[1].y = y2;
a[2].x = x2;a[2].y = y1;
b[0].x = x3;b[0].y = y3;
b[1].x = x3;b[1].y = y4;
b[2].x = x4;b[2].y = y4;
b[3].x = x4;b[3].y = y3;
double s1 = SPIA(a,b,3,4);
printf("%.8f\n",fabs(s1));
}
return 0;
}