路还很长啊~
A Simple Arithmetic
水题, 但是我傻逼了, 把大于long long 值赋给 ll变量了 , 这题唯一注意的就是 -9223372036854775808 -1, 正数ll 最大值比负数绝对值小1
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
int main()
{
ll a, b;
while(~scanf("%lld%lld", &a, &b))
{
if(a == ((ll)1<<63) && b == -1)
{
puts("9223372036854775808");
}
else if((a < 0 && b > 0 || a > 0 && b < 0) && a%b)
{
ll ans = a/b;
printf("%lld\n", ans-1);
}
else
{
ll ans = a/b;
printf("%lld\n", ans);
}
}
return 0;
}
B Broken Counter
————————————————————————————————————————————————
C Determinant
————————————————————————————————————————————————
D Dynamic Graph
———————————————————————————————————————————————
给你一个DAG图,每个节点不是白的就是黑的,有q次操作,每次将点x变换颜色.然后输出当前有多少对白点
方法1:转自tabris
考虑到DAG图,所以进行拓扑排序,
每次操作完,之后将黑色的点去掉,然后进行拓扑序,在过程中记录能到达每个点的个数,然后一路算过去就行了,然后去个重复采用vis标记,复杂度是
O(Tqnm)
,然后妥妥的TLE了,
最后想到用二进制来优化,每一位对应表示每一个节点,然后进行TOP过程中与一下就行了.
可以用5个64位整形计算 (这时候注意求1的个数的时候要用lowbit优化)
也可以用bitset来计算
最后复杂度为 O(Tqnm/a) ,其中 a 为bitset优化的常数,因为|操作如果用数组实现, 是要for一边,on, 每条边都要这样,但是bitset会有优化~
这种算法的具体解释是, bit[x]里的每个1代表,有哪些白点能到达x这个白点, 这样拓扑遍历, 能到达他的祖先的点, 肯定也能到达他的子节点, 所以就是bit[to] = bit[to] | bit[u], 最后for一边记录所有能到达这个点的点有几个,就是有几对
代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <bitset>
#include <queue>
using namespace std;
const int maxn = 305 + 5;
int n, m, q, x, y, deg[maxn], temp[maxn], book[maxn], col[maxn];
vector<int> v[maxn];
void TOP()
{
bitset<maxn> bit[maxn];
int ans = 0;
queue<int> q;
for(int i = 1; i <= n; i++)
{
if(!deg[i])
q.push(i);
temp[i] = deg[i];
}
for(int i = 1; i <= n; i++)
bit[i][i] = 1;
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i = 0; i < v[u].size(); i++)
{
int to = v[u][i];
if(!col[u] && !col[to])
bit[to] = bit[to] | bit[u];
temp[to]--;
if(!temp[to]) q.push(to);
}
}
for(int i = 1; i <= n; i++)
ans += bit[i].count()-1;
printf("%d\n", ans);
}
int main()
{
while(~scanf("%d%d%d", &n, &m, &q))
{
memset(col, 0, sizeof(col));
memset(deg, 0, sizeof(deg));
for(int i = 1; i <= n; i++)
v[i].clear();
for(int i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
v[x].push_back(y);
deg[y]++;
}
while(q--)
{
scanf("%d", &x);
col[x] = !col[x];
TOP();
}
}
return 0;
}
方法2,转自牛逼队友shilongbao(话说我们三个彼此菜逼/牛逼称号换的有点频繁啊
这个题也可以有两种做法,首先我们最基本的暴力去做肯定超时,复杂度O(n*m*q),
官方题解给出的做法就是,我们可以记录f[x][y]为x到y的路径条数,然后维护这个路径.
当要将v变为黑点时 f[x][y]-=f[x][v]*f[v][y],反之变白就是+
。
最后O(n2)枚举任意两点之间的路径条数,>0就是一对点.
总结下, 这里将两个白点转换成能否到达, 最开始n3, 枚举中间点,找出所有两个点之间的路径数, 有黑点,就代表
以他为某个中间点的所有路都没了~这种思维方式还需要加强,以后遇到图论删点,加点都可以想想这种方法吧
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int maxn = 350;
LL dp[maxn][maxn], vis[maxn];
int N, M, Q;
void init()
{
for(int i = 1; i <= N; i++)
{
vis[i] = 0;
for(int j = 1; j <= N; j++)
dp[i][j] = 0;
}
}
int main ()
{
while(~scanf("%d %d %d", &N, &M, &Q))
{
init();
for(int i = 1; i <= M; i++)
{
int u, v;
scanf("%d %d", &u, &v);
dp[u][v]++;
}
for(int k = 1; k <= N; k++ )
for(int st = 1; st <= N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] += dp[st][k] * dp[k][ed];
for(int i = 1; i <= Q; i++)
{
int u;
scanf("%d", &u);
if(vis[u] == 0)
{
vis[u] = 1;
for(int st = 1; st <=N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] -= dp[st][u] * dp[u][ed];
}
else
{
vis[u] = 0;
for(int st = 1; st <=N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] += dp[st][u] * dp[u][ed];
}
int ans = 0;
for(int st = 1; st <= N; st++)
{
if(vis[st]) continue;
for(int ed = 1; ed <= N; ed++)
{
if(vis[ed]) continue;
if(dp[st][ed] > 0) ans++;
}
}
printf("%d\n", ans);
}
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long LL;
const int maxn = 350;
LL dp[maxn][maxn], vis[maxn];
int N, M, Q;
void init()
{
for(int i = 1; i <= N; i++)
{
vis[i] = 0;
for(int j = 1; j <= N; j++)
dp[i][j] = 0;
}
}
int main ()
{
while(~scanf("%d %d %d", &N, &M, &Q))
{
init();
for(int i = 1; i <= M; i++)
{
int u, v;
scanf("%d %d", &u, &v);
dp[u][v]++;
}
for(int k = 1; k <= N; k++ )
for(int st = 1; st <= N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] += dp[st][k] * dp[k][ed];
for(int i = 1; i <= Q; i++)
{
int u;
scanf("%d", &u);
if(vis[u] == 0)
{
vis[u] = 1;
for(int st = 1; st <=N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] -= dp[st][u] * dp[u][ed];
}
else
{
vis[u] = 0;
for(int st = 1; st <=N; st++)
for(int ed = 1; ed <= N; ed++)
dp[st][ed] += dp[st][u] * dp[u][ed];
}
int ans = 0;
for(int st = 1; st <= N; st++)
{
if(vis[st]) continue;
for(int ed = 1; ed <= N; ed++)
{
if(vis[ed]) continue;
if(dp[st][ed] > 0) ans++;
}
}
printf("%d\n", ans);
}
}
return 0;
}
E Longest Increasing Subsequence
——————————————————————————————————————————————
好题!题目大意:
给你N个数组成的序列,现在规定F【i】表示的是以a【i】结尾的最长上升子序列长度。
LIS(1)表示的是删除第一个数之后,F【i】的亦或和。
现在然你求LIS(1~N);
题意有点绕, 删除第i个数, 对i前面的数没有影响, 对后面的数影响最多就是-1,那么什么时候-1呢?
转自mengxiang000
然后分两种情况考虑:
①当F【j】依旧为F【j】的时候,那么说明位子j前边存在某些位子使得F【x】=F【j】-1&&a【x】<a【j】&&x!=i(删除的位子);
②当F【j】为F【j】-1的时候,就说明位子j前边没有存在那样的位子。
所以我们过程维护一个数组 s【Z】表示F值为Z的位子上最小的a【】;
就能贪心的使得s【Z】尽可能的小,那么满足F【j】依旧为F【j】的情况就越来越多了。
简而言之, 就是看长度为j前面F【j】-1的最小末尾数字是多少,如果大于等于a[j],说明删掉那个是唯一一个长度为F【j】-1,并且小于a【j】的,
所以要维护每个长度的最小值。
本来只会n2log算法, nlog的时候,不要删除的那个数字,却没有观察题目...删除一个,最多就影响后面的, 当只有他这一个选择的时候才会影响。。这里存下长度的末尾最小值比较屌。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int INF = 5e3 + 5;
const int maxn = 5e3 + 5;
typedef long long ll;
int dp[maxn], minx[maxn], a[maxn];
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n; i++)
{
dp[i] = 1;
for(int j = 1; j < i; j++)
if(a[j] < a[i])
dp[i] = max(dp[i], dp[j]+1);
}
for(int k = 1; k <= n; k++)
{
for(int i = 1; i < maxn; i++) minx[i] = INF;
ll ans = 0;
for(int i = 1; i <= n; i++)
{
if(i == k) continue;
int temp = dp[i];
if(i > k && a[i] <= minx[dp[i]-1]) temp = dp[i]-1;
ans ^= (temp*temp);
minx[temp] = min(minx[temp], a[i]);
}
printf("%lld%c", ans, k == n ? '\n' : ' ');
}
}
return 0;
}
F Simple Algebra
————————————————————————————————————————————————
给你
f(x,y)=ax2+bxy+cy2
,问你是否永远非负
分类讨论a,b,c是否为0的情况
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
int main()
{
ll a, b, c, d;
while(cin >> a >> b >> c)
{
if(a < 0) puts("No");
else if(a == 0)
{
if(b == 0)
{
if(c >= 0) puts("Yes");
else puts("No");
}
else puts("No");
}
else
{
if(b*b-4*a*c <= 0) puts("Yes");
else puts("No");
}
}
return 0;
}
G 2017
水
H Roads
————————————————————————————————————————————————
I Strange Prime
————————————————————————————————————————————————
J Skewness
————————————————————————————————————————————————
给你一个n*n的方阵 求每个子矩阵的元素和的三次方的和
思路:三次方很暴力很麻烦,掌握一次方的求法就好了
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
a[i][j] = a[i][j] * i * (n-i+1) * j * (n-j+1); //上下左右的情况,+1,是表示可以枚举的格子
K 2017 Revenge
———————————————————————————————————————————————
L Nice Trick
————————————————————————————————————————————————
给你一个序列以及
问你 ∑i≤i<j<k<l≤naiajakal
水题......这么水的题....思路被带歪了, 他告诉你on求三个数相乘, 4个数相乘, 就是枚举第四个数,乘上前i个数三个相乘的情况,也就是上面的公式- -
利用公式的代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 5;
typedef long long ll;
ll a[maxn], inv6;
ll quick_mul(ll x, ll n)
{
ll ans = 1;
while(n)
{
if(n&1) ans = (ans*x)%Mod;
x = (x*x)%Mod;
n /= 2;
}
return ans;
}
ll cal(ll a1, ll a2, ll a3)
{
ll ans = 0;
ans += quick_mul(a1,3);
ans -= a1*a2%Mod*3%Mod;
ans = (ans % Mod + Mod ) % Mod;
ans += a3*2%Mod;
return ans*inv6%Mod;
}
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
ll ans = 0;
ll sum1 = (a[1] + a[2] + a[3])%Mod;
ll sum2 = ((a[1]*a[1])%Mod + (a[2]*a[2])%Mod + (a[3]*a[3])%Mod)%Mod;
ll sum3 = ((quick_mul(a[1], 3) + quick_mul(a[2],3))%Mod+quick_mul(a[3],3))%Mod;
inv6 = quick_mul((ll)6, Mod-2);
for(int i = 4; i <= n; i++)
{
ans = (ans+(cal(sum1, sum2, sum3)*a[i])%Mod)%Mod;
sum1 = (sum1 + a[i])%Mod;
sum2 = (sum2 + (a[i]*a[i])%Mod)%Mod;
sum3 = (sum3 + quick_mul(a[i],(ll)3))%Mod;
}
printf("%lld\n", ans);
}
return 0;
}
这题dp也很简单啊, dp[i][j]代表前i个数选j个相乘, 方程就是
dp[i][j] = (dp[i-1][j]+a[i]*dp[i-1][j-1])%mod; 第i个选还是不选,很常规的二维dp....
dp代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
ll a[maxn], dp[maxn][5];
int main(void)
{
int n;
while(cin >> n)
{
for(int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
memset(dp, 0, sizeof(dp));
dp[1][1] = a[1];
for(int i = 2; i <= n; i++)
{
for(int j = 1; j <= 4; j++)
{
if(j == 1) dp[i][j] = (dp[i-1][j]+a[i])%mod;
else dp[i][j] = (dp[i-1][j]+a[i]*dp[i-1][j-1])%mod;
}
}
printf("%lld\n", dp[n][4]);
}
return 0;
}