欢迎回来
题意:有 2N 2 N 根烤肉扦,第 i i 个烤肉扦长度为 Li L i ,每一块肉需要横跨两根烤肉扦,长度为 1 1 。可以随意分配烤肉扦的顺序,询问最多同时可以烤多少块肉。
N≤100,Li≤100,Li N ≤ 100 , L i ≤ 100 , L i 是整数
解答:将烤肉扦从小到大排序,下标为奇数的位置之和即为答案。
#include
#define N 1050
using namespace std;
int a[N],n,ans;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int main() {
n = rd() * 2;
for (int i=1;i<=n;i++) a[i] = rd();
sort(a+1,a+n+1);
for (int i=1;i<=n;i+=2) ans += a[i];
cout << ans << endl;
return 0;
}
题意:有一个边长为 N N 的正三角形,顶点分别为 a a 、 b b 、 c c ,有一束激光从 ab a b 边上,距离 a a 为 x x 的位置,平行于 bc b c 射入正三角形。激光遇到三角形的三边或者已经走过的光路时,会发生镜面反射。询问最后激光回到出发点时,走过光路的长度。
2≤N≤1012,1≤X≤N−1 2 ≤ N ≤ 10 12 , 1 ≤ X ≤ N − 1
解答:答案为 3(N−gcd(N,X)) 3 ( N − g c d ( N , X ) )
原图形走两步就会变成一个平行四边形,两边分别为 (X,N−X) ( X , N − X ) ,这两步的长度为N
考虑这样一个长宽为 a a 、 b b 平行四边形,设需要的步数为 F(a,b) F ( a , b )
那么有这样一个关系式:
若 a>b a > b , F(a,b)=F(b,a) F ( a , b ) = F ( b , a )
若 a=b a = b , F(a,b)=a=b F ( a , b ) = a = b
若 a<b a < b , F(a,b)=2a+F(b−a,b) F ( a , b ) = 2 a + F ( b − a , b )
这样我们就已经得到一个log级别的递推算法了
我们还可以对这个式子化简
我们定义 phi(F(a,b))=a+b p h i ( F ( a , b ) ) = a + b
当a ≠b ≠ b 时, phi p h i 每下降 x x , F F 的值会增加 2x 2 x
当 a=b a = b 时, phi p h i 每下降 2x 2 x , F F 的值会增加 x x
加上最开始走的两部,可以得知答案为 3(N−gcd(N,X)) 3 ( N − g c d ( N , X ) )
#include
using namespace std;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int main() {
long long n,m;
cin >> n >> m;
cout << 3LL * (n-__gcd(n,m));
return 0;
}
题意:给定一棵 N N 个节点的树,至少移除多少个节点,使得该树的直径 ≤K ≤ K 。
2≤N≤2000 2 ≤ N ≤ 2000 ; 1≤K≤N−1 1 ≤ K ≤ N − 1 ; 1≤Ai≤N 1 ≤ A i ≤ N ; 1≤Bi≤N 1 ≤ B i ≤ N
解答:若一个树的直径小于等于 K K ,则一定存在一条边,使得删除这条边之后,以边的两个端点为根的子树深度分别小于等于 ⌈K−12⌉ ⌈ K − 1 2 ⌉ 和 ⌊K−12⌋ ⌊ K − 1 2 ⌋ 。爆枚这条边再dfs判深度即可,时间复杂度 O(N2) O ( N 2 )
#include
#define N 100050
#define INF (1<<29)
using namespace std;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int a[N],b[N],n,k,tot;
vector<int> E[N];
void dfs(int u,int f,int d) {
if (d > k/2) return ;
tot++;
for (int i=0;i<(int)E[u].size();i++) {
int v = E[u][i];
if (v == f) continue;
dfs(v,u,d+1);
}
}
int main() {
n = rd(), k = rd();
for (int i=1;iint u = rd(), v = rd();
a[i] = u, b[i] = v;
E[u].push_back(v);
E[v].push_back(u);
}
int ans = INF;
if (k%2 == 0) {
for (int i=1;i<=n;i++) {
tot = 0;
dfs(i, i ,0);
ans = min(ans, n - tot);
}
} else {
for (int i=1;i0;
dfs(a[i], b[i], 0);
dfs(b[i], a[i], 0);
ans = min(ans, n - tot);
}
}
cout << ans << endl;
return 0;
}
题意:
给定一个长度为 M M ,和为 N N 的序列{ A A },需要重排 A A 并且构造一个数列{ B B }满足:
1、{ B B }的元素和为 N N
2、任何序列满足 (1) ( 1 ) 和 (2) ( 2 ) 的都满足 (3) ( 3 )
(1)序列的这些元素是回文串:前 A1 A 1 个项,接下来 A2 A 2 项,接下来 A3 A 3 项……
(2)序列的这些元素是回文串:前 B1 B 1 个项,接下来 B2 B 2 项,接下来 B3 B 3 项……
(3)该序列的所有元素相同
1≤N≤105 1 ≤ N ≤ 10 5
1≤M≤100 1 ≤ M ≤ 100
Ai≤105 A i ≤ 10 5
解答:
转化一下模型,有一排 N N 个点,长度为 L L 的回文串相当于连接 ⌊L2⌋ ⌊ L 2 ⌋ 条无向边,要求最后所有的点都在一个联通分量里面。
显然至少需要 N−1 N − 1 条边,那么无解的情况我们就可以通过这个来判断了。如果 A A 中的奇数项大于等于3,无解。
单独讨论一下 M=1 M = 1 的情况
当 M≥2 M ≥ 2 时,将 A A 中的奇数项移动到两端, B1=A1−1 B 1 = A 1 − 1 , BM=AM−1 B M = A M − 1 ,其余的 Bi=Ai B i = A i 即可。
#include
#define N 1000500
using namespace std;
int a[N],b[N],n,m,cnt,t;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
int main() {
n = rd(), m = rd();
for (int _=1;_<=m;_++) a[_] = rd();
sort(a+1,a+m+1, greater<int>() );
if (m==1) {
if (n==1) {printf("1\n1\n1\n"); return 0;}
printf("%d\n",a[1]);
printf("2\n");
printf("%d %d\n",a[1]-1,1);
return 0;
}
for (int _=1;_<=m;_++) (a[_]&1) ? ++cnt : 0;
if (cnt > 2) return puts("Impossible"), 0;
for (int _=2;_<=m-1;_++)
if (a[_]&1) (a[1]&1) ? swap(a[_],a[m]) : swap(a[_],a[1]);
for (int _=1;_<=m;_++) printf("%d%c",a[_],_==m?'\n':' ');
(a[1]==1) ? (t=2) : (t=1);
printf("%d\n",m-t+1);
memcpy(b,a,sizeof(a));
b[1]--, b[m]++;
for (int _=t;_<=m;_++) printf("%d%c",b[_],_==m?'\n':' ');
return 0;
}
题意:
给定 N N 个数{ A A }和{ B B },求
#include
#define N 4050
#define c 2010
#define mod 1000000007
using namespace std;
typedef long long LL;
int n,a[50*N],b[50*N],F[N][N],jc[5*N];
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
inline void inc(int &x,int y) {x=(x+y)%mod;}
inline int qp(int a,int b) {
int ret = 1;
while (b) {
if (b&1) ret = 1LL * ret * a % mod;
b >>= 1, a = 1LL * a * a % mod;
}
return ret;
}
inline int C(int a,int b) {
return 1LL * jc[a] * qp(jc[b], mod-2) % mod * qp(jc[a-b], mod-2) % mod;
}
int main() {
n = rd();
for (int _=1;_<=n;_++) a[_] = rd(), b[_] = rd();
for (int _=1;_<=n;_++) F[c-a[_]][c-b[_]]++;
for (int x=c-2000;x<=c+2000;x++)
for (int y=c-2000;y<=c+2000;y++)
inc(F[x][y], F[x-1][y]), inc(F[x][y], F[x][y-1]);
jc[0] = 1;
for (int i=1;i<=8000;i++) jc[i] = 1LL * jc[i-1] * i % mod;
int ans = 0;
for (int _=1;_<=n;_++) inc(ans, F[ c+a[_] ][ c+b[_] ]);
for (int _=1;_<=n;_++) inc(ans, mod - C(2*a[_]+2*b[_], 2*a[_]) );
ans = 1LL * ans * qp(2, mod-2) % mod;
cout << ans << endl;
return 0;
}
题意:
给定一个长度为 N N 的排列 P P ,对于 i i , j j 满足 j−i≥K j − i ≥ K 且 |Pi−Pj|=1 | P i − P j | = 1 就可以交换这两个元素,求字典序最的序列。
解答:
阅读和参考了官方题解以及
https://www.cnblogs.com/BearChild/p/7895719.html
令 Q Q 为 P P 的转置,即 QPi=i Q P i = i 。
由字典序的性质可得,当 Q Q 字典序最小时, P P 字典序最小。
对排列 P P ,只有在两个元素相邻并且权值差不小于 K K 时才能交换。
由此得出一个性质,当两元素 Px P x 和 Py P y ( x<y x < y )的权值差小于 K K 时,无论怎么操作 Px P x 元素始终在 Py P y 元素前面。
显然,满足所有上述限制的排列,一定能够通过合法的交换得到。
那么对于权值差小于 K K 的两个元素 Px P x 和 Py P y ,我们从 Px P x 向 Py P y 连一条边,那么一组合法解便满足该图的拓扑序。
这么连边的边数是 O(N2) O ( N 2 ) ,下面优化连边:
形如A–>B, B–>C, A–>C的连边中,A–>C这条边显然在拓扑关系中无用。
我们考虑如何避免加入 A–>C 这种边:将 Pi P i 连向 (Pi−K,Pi) ( P i − K , P i ) 和 (Pi,Pi+K) ( P i , P i + K ) 两个区间下标最小的那一个即可。线段树维护区间最小值。
时间复杂度: O(NlogN) O ( N l o g N )
#include
#define N 500050
#define INF (1<<29)
#define mid ((l+r)>>1)
#define ls l,mid,t<<1
#define rs mid+1,r,t<<1^1
using namespace std;
inline int rd() {
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
priority_queue<int, vector<int>, greater<int> >q;
int n,k,e[N],a[N],p[N],d[N];
int x,ll,rr,res;
int tr[4*N];
vector<int> E[N];
void query(int l,int r,int t) {
if (!tr[t]) return ;
if (l >= ll && r <= rr) {
if (res == 0 || p[ tr[t] ] < p[res] )
res = tr[t];
return ;
}
if (ll <= mid) query(ls);
if (rr > mid) query(rs);
}
void update(int l,int r,int t) {
if (l > x || r < x) return ;
if (l == r) {tr[t] = x; return ;}
update(ls), update(rs);
if (p[tr[t<<1]] < p[tr[t<<1^1]])
tr[t] = tr[t<<1];
else
tr[t] = tr[t<<1^1];
}
bool cmp(int p1,int p2) {return p[p1] < p[p2];}
int main() {
n = rd(), k = rd();
for (int _=1;_<=n;_++) p[_] = rd();
p[0] = INF;
for (int _=1;_<=n;_++) e[_] = _;
sort(e+1,e+n+1,cmp);
for (int _=n;_>=1;_--) {
x = e[_];
ll = x - k + 1, rr = x, res = 0;
query(1,n,1);
if (res)
E[x].push_back(res), d[res]++;
ll = x, rr = x + k - 1, res = 0;
query(1,n,1);
if (res)
E[x].push_back(res), d[res]++;
update(1,n,1);
}
for (int _=1;_<=n;_++) if (!d[_]) q.push(_);
for (int _=1;_<=n;_++) {
int x = q.top(); q.pop();
a[x] = _;
for (int i=0;i<(int)E[x].size();i++)
if (--d[ E[x][i] ]==0) q.push(E[x][i]);
}
for (int _=1;_<=n;_++) printf("%d\n",a[_]);
return 0;
}