#include<iostream>#include<algorithm>#include<cstring>usingnamespace std;intgcd(int a,int b){
if(b ==0)return a;returngcd(b, a % b);}int a[10], b[10];intmain(){
for(int i =1; i <=6; i++)scanf("%d",&a[i]);for(int i =1; i <=6; i++)scanf("%d",&b[i]);int res =0;for(int i =1; i <=6; i++){
for(int j =1; j <=6; j++){
res += a[i]> b[j];}}int d =gcd(res,36);
res /= d;printf("%d/%d\n", res,36/ d);return0;}
#include<iostream>#include<cstring>#include<algorithm>usingnamespace std;constint maxn =100010, maxm =200010;int h[maxn], e[maxm], ne[maxm], w[maxm], idx;int sz[maxn];bool is_candidate[maxn], ok[maxn];voidadd(int a,int b,int c){
e[idx]= b, ne[idx]= h[a], w[idx]= c, h[a]= idx++;}int N, K, ans;intdfs(int u,int fa){
sz[u]= is_candidate[u]?1:0;for(int i = h[u]; i !=-1; i = ne[i]){
int v = e[i];if(v == fa)continue;
sz[u]+=dfs(v, u);}return sz[u];}voiddfs2(int u,int fa){
if(is_candidate[u]) ok[u]=true;for(int i = h[u]; i !=-1; i = ne[i]){
int v = e[i];if(v == fa)continue;if(sz[v]&& K - sz[v]) ok[u]=true;dfs2(v, u);}}intmain(){
memset(h,-1,sizeof h);scanf("%d%d",&N,&K);for(int i =1; i < N; i++){
int a, b, c;scanf("%d%d%d",&a,&b,&c);add(a, b, c),add(b, a, c);}for(int i =1; i <= K; i++){
int x;scanf("%d",&x);
is_candidate[x]=true;}dfs(1,-1);dfs2(1,-1);for(int i =1; i <= N; i++){
ans += ok[i]?1:0;}printf("%d\n", ans);return0;}
我们这样思考,假设第一个位置 a[1] 就是等差序列的起点,那么我们可以计算每一个点的移动,记录最多向左移动的距离 lmost,和最多向右移动的距离 rmost。如果我们要把等差序列起点左移的话,rmost一定会增加。如果右移的话,lmost 必然会增加。因为我们只关心最大值。那么,我们可知最小化的最大值就是 l m o s t + r m o s t 2 \frac{lmost + rmost}{2} 2lmost+rmost. 但是我们也要注意的是,可能是个递减序列,因此要把 D ∗ = − 1 D *= -1 D∗=−1,然后再跑一遍上述过程。
然后,神奇的题目,用 GNU C++17 (64) 会莫名其妙超时,我查看了一下,答案已经输出了,但是似乎检测出了问题?评测姬快来背锅
#include<iostream>#include<algorithm>#include<cstring>usingnamespace std;constint maxn =1000010;typedeflonglong ll;
ll a[maxn], D;int N;
ll f(){
ll lmost =0, rmost =0;for(int i =1; i <= N; i++){
ll x = a[1]+(i -1LL)* D;if(x > a[i]) rmost =max(rmost, x - a[i]);if(x < a[i]) lmost =max(lmost, a[i]- x);}return(lmost + rmost)/2;}intmain(){
scanf("%d%lld",&N,&D);
D *=10;for(int i =1; i <= N; i++){
scanf("%lld",&a[i]);
a[i]*=10;}
ll ans =f();
D =-D;
ans =min(ans,f());printf("%lld.%lld\n", ans /10, ans %10);return0;}
我们想想充要条件是什么。从第一条直线和第三条直线上分别任取一点 x 1 , x 3 x_1, x_3 x1,x3. 那么,如果第二条直线上有一点可以和 x 1 , x 3 x_1, x_3 x1,x3 共线,等价于 x 2 = x 1 + x 3 2 x_2 = \frac{x1 + x3}{2} x2=2x1+x3. 因此,我们看看第一条直线和第三条直线可以有多少种组合形式。那么,是不是可以看成两个多项式的卷积啊。设 f = a ∗ c f = a * c f=a∗c,然后答案就是 ∑ b [ i ] ∗ f [ 2 ∗ i ] \sum\limits b[i]* f[2*i] ∑b[i]∗f[2∗i].
不过傅里叶变换一定要小心细节。(1)数组中存的是多项式的系数。(2)最后a.x一定要除以 tot 才是卷积的系数。(3)数组开多大取决于数的取值范围,而且一定要大于2的整数幂。
由此可见,计数类问题,有时候可以用FFT解决。因为计数类问题有时候可以转化为多项式得卷积。
#include<iostream>#include<algorithm>#include<cstring>#include<cmath>usingnamespace std;constdouble PI =acos(-1);constint maxn =300010;typedeflonglong ll;struct Complex {
double x, y;
Complex operator+(const Complex& t)const{
return{
x + t.x, y + t.y };}
Complex operator-(const Complex& t)const{
return{
x - t.x, y - t.y };}
Complex operator*(const Complex& t)const{
return{
x * t.x - y * t.y, x * t.y + y * t.x };}}a[maxn], c[maxn];int rev[maxn], bit, tot;int b[maxn];voidfft(Complex a[],int inv){
for(int i =0; i < tot; i++){
if(i < rev[i])swap(a[i], a[rev[i]]);}for(int mid =1; mid < tot; mid <<=1){
auto w1 =Complex({
cos(PI / mid), inv *sin(PI / mid)});for(int i =0; i < tot; i += mid *2){
auto wk =Complex({
1,0});for(int j =0; j < mid; j++, wk = wk * w1){
auto x = a[i + j], y = wk * a[i + j + mid];
a[i + j]= x + y, a[i + j + mid]= x - y;}}}}int dif =30000;intmain(){
int N1, N2, N3;scanf("%d",&N1);for(int i =1; i <= N1; i++){
int x;scanf("%d",&x);
a[x + dif].x +=1;}scanf("%d",&N2);for(int i =1; i <= N2; i++){
int x;scanf("%d",&x);
b[x + dif]+=1;}scanf("%d",&N3);for(int i =1; i <= N3; i++){
int x;scanf("%d",&x);
c[x + dif].x +=1;}
bit =17;
tot =(1<< bit);for(int i =0; i < tot; i++){
rev[i]=(rev[i >>1]>>1)|((i &1)<<(bit -1));}fft(a,1),fft(c,1);for(int i =0; i < tot; i++) a[i]= a[i]* c[i];fft(a,-1);
ll ans =0;//一定要小心,这里是数据范围最大,即60000//而且,卷积的结果在 a 中,要除以 tot 才对。而且,四舍五入的话(比如round),不要加上 0.5,+0.5意味着舍弃小数部分。for(int i =0; i <=60000; i++){
ll x = a[2* i].x / tot +0.5;
ans += x * b[i];}printf("%lld\n", ans);return0;}
I. Stock Analysis
给一个序列 ( n ≤ 2000 ) (n\le 2000) (n≤2000),给 Q ( Q ≤ 20000 ) Q(Q\le20000) Q(Q≤20000) 组询问 ( l , r , u ) (l, r, u) (l,r,u),问在 ( l , r ) (l, r) (l,r) 内,不大于 u u u 的最大连续子段和是多少?
#include<iostream>#include<cstring>#include<algorithm>#include<vector>usingnamespace std;typedeflonglong ll;const ll INF =1e18;constint maxn =2010, maxm =200010;struct node {
int l, r, id;
ll val;};
vector<node> q;
ll w[maxn], tr[maxn][maxn], ans[maxm];intlowbit(int x){
return x &-x;}boolcmp(const node& u,const node& v){
return u.val < v.val || u.val == v.val && u.id < v.id;}int N, M;voidupdate(int x,int y, ll v){
while(x){
int z = y;while(z <= N){
tr[x][z]=max(tr[x][z], v);
z += z &-z;}
x -= x &-x;}}
ll query(int x,int y){
ll res =-INF;while(x <= N){
int z = y;while(z){
res =max(res, tr[x][z]);
z -= z &-z;}
x += x &-x;}return res;}intmain(){
scanf("%d%d",&N,&M);for(int i =1; i <= N; i++){
fill(tr[i], tr[i]+ N +1,-INF);}for(int i =1; i <= N; i++){
scanf("%lld",&w[i]);
w[i]+= w[i -1];}for(int i =1; i <= N; i++){
for(int j = i; j <= N; j++){
q.push_back({
i, j,0, w[j]- w[i -1]});}}for(int i =1; i <= M; i++){
int l, r;
ll x;scanf("%d%d%lld",&l,&r,&x);
q.push_back({
l, r, i, x });}sort(q.begin(), q.end(), cmp);for(auto p : q){
int l = p.l, r = p.r, id = p.id;
ll val = p.val;if(!id)update(l, r, val);else ans[id]=query(l, r);}for(int i =1; i <= M; i++){
if(ans[i]!=-INF)printf("%lld\n", ans[i]);elseprintf("NONE\n");}return0;}
J. Switches
题意:已知有 n n n 个开关和 n n n 盏灯,现在每一个开关可以控制若干盏灯,该信息用矩阵表示。一盏灯要亮,当且仅当这盏灯对应的开关数量为奇数。问对于每一盏灯,能否打开若干个开关,使得只有该盏灯是亮的,而其他灯都是灭的。若可以,输出每一盏灯对应的开关,否则输出 − 1 -1 −1。
注意题目输入,说的是第 i 行表示第 i 个开关控制那些灯。而我们在列异或方程组的时候,每一行应该是都有哪些开关控制着第 i 个灯。因此要把输入矩阵转置一下。
#include<iostream>#include<cstring>#include<algorithm>#include<bitset>usingnamespace std;constint maxn =510;
bitset<maxn *2> a[maxn];
bitset<maxn> b, inv_a[maxn], ans;int N;boolGauss_inv(){
for(int c =1; c <= N; c++){
int t = c;for(int i = c; i <= N; i++){
if(a[i][c]){
t = i;break;}}if(a[t][c]==0)returnfalse;swap(a[t], a[c]);//小心这个地方要从第一行开始看。大雪菜那个是从 r + 1 行开始,那是因为他没有化成单位矩阵//但是求逆矩阵必须要化为单位阵。for(int i =1; i <= N; i++){
if(a[i][c]&& i != c){
a[i]^= a[c];}}}return1;}// A = B * Cvoidmul(bitset<maxn>& a, bitset<maxn> b[], bitset<maxn>& c){
for(int i =1; i <= N; i++){
//小心这个地方是与,不是异或。矩阵乘法怎么可以写异或呢!
a[i]=(b[i]& c).count()%2;}}intmain(){
scanf("%d",&N);for(int i =1; i <= N; i++){
a[i][i + N]=1;for(int j =1; j <= N; j++){
int x;scanf("%d",&x);if(x) a[j][i]=1;}}if(!Gauss_inv()){
printf("-1\n");}else{
for(int i =1; i <= N; i++){
for(int j =1; j <= N; j++) inv_a[i][j]= a[i][j + N];}/*for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) cout << a[i][j + N] << " ";
cout << endl;
}*/for(int i =1; i <= N; i++){
b.reset();
b[i]=1;
ans.reset();mul(ans, inv_a, b);for(int i =1; i <= N; i++){
if(ans[i])printf("%d ", i);}printf("\n");}}return0;}
K. Tiling Polyomino
挖坑。找到题解或者看懂代码再补
L. Two Buildings
题意:给定长度为 n n n 的数组 h h h,要求计算 m a x { ( h [ i ] + h [ j ] ) ∗ ( j − i ) } max\{(h[i]+h[j])*(j-i)\} max{
(h[i]+h[j])∗(j−i)},其中 i < j i<j i<j。数据范围: n < = 1 e 6 , 1 < = h ( i ) < = 1 e 6 n<=1e6,1<=h(i)<=1e6 n<=1e6,1<=h(i)<=1e6.