首先如果2的次幂是减号的话, 我们可以将2的次幂这个等比数列求出来。 根据等比数列求和公式 a 1 ( 1 − q n ) 1 − q \frac{a1(1 - q^n)}{1-q} 1−qa1(1−qn), 将公比2代入, 将a1为1 代入, 可得 ∑ i = 0 n 2 i = 2 n + 1 − 1 \sum_{i=0}^{n}2^i = 2^{n+1} - 1 ∑i=0n2i=2n+1−1。那么, 原题我们就可以将答案减去这个2的次幂和了 再来考虑剩下的: 原题中剩下的都是正的, 为1到n中不是2的次幂和的数之和。根据容斥, 所以剩下的和等于1-n之和减去2的次幂和。 那么答案就等于1-n之和减去两倍的2的次幂和。 1-n之和等于 ( 1 + n ) ∗ n / 2 (1+n) * n / 2 (1+n)∗n/2; 原题得解
可以很轻松容易的发现一个操作中奇特的性质。 对于一个区间[l, r], 将它后k个字符放到开头来, 相当于: 在一个区间为[0, r - l]的字符串里, 将[l,r]赋值给这个区间, 然后对于这个新区间`的每个字符,(设下标为i), 都变换成了(i+k) % (r - l + 1) 原理是因为前面的字符由于尾部的k个字符移到了前面来, 所以前进k; 然而后面的字符加上k后在%(r-l+1)就自动的到了前面了。 注:大佬们都用的rotate函数, 可以参考学习。
首先按照坐标进行极角排序, 如何排呢? atan2函数:输入坐标(double)y, x, 返回向量(x,y)与x轴的夹角(弧度制)。 容易想到, 一二象限函数值为整, 三四象限函数值为负, 所以此函数值域为 [ − π , π ] [-\pi, \pi] [−π,π]。 那么根据此函数, 我们可以想到排序后的向量应该是这个样子: 那么对于夹角最小的向量就是相邻两个的向量, 扫一边维护最小值即可。
首先,有三种情况:两个向量与x轴夹角:1.为负;2.为正; 3.为一正一负; 对于每一个图, 可见两者夹角(中间的圆圈)为p[i+1]的atan2 - p[i]的atan2; 还有, 注意两者夹角如果大于 π \pi π的话, 要变成 2 π 2\pi 2π减去当前角; 大佬做法:用vector里面加上一个atan2与向量编号的pair维护
这个题实际上一眼就可以看出是搜索。所以只需要把每个联通块儿周围的求出来即可。 真没什么好说的。。。 联通块标个号一累加即可
#include<bits/stdc++.h> using namespace std; const int dx[4] = {0, 1, -1, 0}; const int dy[4] = {1, 0, 0, -1}; int n, m, k; bool mp[1010][1010]; int vis[1010][1010]; int ans[1010 * 1010]; int sum = 0; int cntblocks = 0; int check(int x,int y) { return x>=0&&x<=n&&y>=0&&y<=m; } void dfs(int x, int y) { //cout << x << y; vis[x][y] = cntblocks; //cout << vis[x][y] << endl; for(int i = 0; i < 4; i++) { int nx = x + dx[i]; int ny = y + dy[i]; //cout << nx << " " << ny << endl; if(check(nx, ny) && !vis[nx][ny]) { if(mp[nx][ny] == 1) { // cout << nx << " " << ny << " "; sum++; // printf("ok\n"); } else { dfs(nx, ny); } //cout << endl; } } } int main() { cin >> n >> m >> k; for(int i = 1; i <= n; i++) { for(int j = 1; j <= m; j++) { char c; cin >> c; if(c == '*') { mp[i][j] = 1; } } } while(k--) { int x, y; cin >> x >> y; if(ans[vis[x][y]]) { cout << ans[vis[x][y]] << endl; continue; } cntblocks++; sum = 0; dfs(x, y); ans[cntblocks] = sum; cout << sum << endl; } return 0; }一看n和m数据范围, 不可能是状压, 由此得出, 一定是高维dp;一顿胡乱操作。 设dp[i][j][k]表示i * j 的巧克力吃掉k块的最小花费 对于任意一块i * j的chocolate, 都有两种切割方式:横着, 竖着。所以我们枚举每一条切割线, 设当前枚举到了切割线c, 假如他是横着的 那么我们的巧克力就被分成了两块:j*c 与 (i - c)*j。那么,将两个的最小花费加起来再加上切割线费用即可。竖着同理, 可切割成i * c与 i * (j-c)两块; 转移方程为: d p [ i ] [ j ] [ k ] = m i n ( d p [ i − c ] [ j ] [ k − p ] + d p [ c ] [ j ] [ p ] + j × j , d p [ i ] [ j − c ] [ k − p ] + d p [ i ] [ c ] [ p ] + i × i ) dp[i][j][k] = min(dp[i-c][j][k-p] + dp[c][j][p] + j \times j \ , dp[i][j-c][k-p] + dp[i][c][p] + i \times i) dp[i][j][k]=min(dp[i−c][j][k−p]+dp[c][j][p]+j×j ,dp[i][j−c][k−p]+dp[i][c][p]+i×i) (p是切下来一块中吃掉的块数) 注意:两个对称的切割线切下来的块数是一样的 所以枚举一半即可。 可以五重循环:i, j, 一共吃掉的块数, 切割线, 切割后其中一块吃掉的块数。 大佬用的记忆化搜索, 将三重循环变量放到递归里枚举, 只枚举切割线长度与切割后其中一块吃掉的块数。这样跑的更快。
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll read(){ ll a=0,b=getchar(),c=1; while(!isdigit(b))c=b=='-'?-1:1,b=getchar(); while(isdigit(b))a=a*10+b-'0',b=getchar(); return a*c; } int T,n,m,k,ans,dp[35][35][55]; int dfs(int x,int y,int z){ if(dp[x][y][z])return dp[x][y][z]; if(x*y==z or z==0)return 0; dp[x][y][z]=1e9; for(int i=1;i<=x-i;i++) for(int j=0;j<=z;j++) dp[x][y][z]=min(dp[x][y][z],dfs(i,y,z-j)+dfs(x-i,y,j)+y*y); for(int i=1;i<=y-i;i++) for(int j=0;j<=z;j++) dp[x][y][z]=min(dp[x][y][z],dfs(x,i,j)+dfs(x,y-i,z-j)+x*x); return dp[x][y][z]; } int main(){ T=read(); while(T--){ n=read(),m=read(),k=read(); printf("%d\n",dfs(n,m,k)); } return 0; } //以下是本菜鸡代码 #include<bits/stdc++.h> using namespace std; long long n, m, k; long long f[60][60][60]; int main() { int t; cin >> t; memset(f, 0x3f, sizeof(f)); //cin >> n >> m >> k; //f[0][0][0] = 0; for(int i = 0; i <= 50; i++) { f[0][0][i] = 0; } for(int i = 1; i <= 30; i++) { for(int j = 1; j <= 30; j++) { if(i * j <= 50) f[i][j][i*j] = 0; f[i][j][0] = 0; } } for(int i = 1; i <= 30; i++) { for(int j = 1; j <= 30; j++) { int p = min(i * j, 50); for(int c = 1; c <= p; c++) { for(int a = 1; a <= i / 2; a++) { int k1 = min(a * j, c); for(int b = 0; b <= k1; b++) f[i][j][c] = min(f[i][j][c], f[i - a][j][c - b] + f[a][j][b] + j * j); } for(int a = 1; a <= j / 2; a++) { int k2 = min(a * i, c); for(int b = 0; b <= k2; b++) { f[i][j][c] = min(f[i][j][c], f[i][a][b] + f[i][j-a][c-b] + i * i); } } } } } while(t--) { int n, m, k; cin >> n >> m >> k; cout << f[n][m][k] << endl; } return 0; }发个题解链接, 是我们机房的一位巨佬, 已经AK IOI了!! 大佬题解
