题目链接:http://codeforces.com/contest/894
A. QAQ
题意:问你有多少个QAQ子序列
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;
cin>>s;
int ans=0;
for(int i=0;i<s.size();i++){
for(int j=i+1;j<s.size();j++){
for(int k=j+1;k<s.size();k++){
if(s[i]=='Q'&&s[j]=='A'&&s[k]=='Q') ans++;
}
}
}
return 0*printf("%d\n", ans);
}
B. Ralph And His Magic Field
题意:给你n*m的矩阵,问你有多少种填数的方案,使得每一行和每一列的乘积都是k
解法:打表找了规律发现是2^{(n-1)*(m-1)},唯一的hack点,无解的情况。当n,m不同为奇偶的时候,k=-1时无解。靠着这个hack点上分。。。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
LL qsm(LL a, LL n){
LL ret=1;
while(n){
if(n&1) ret=(ret%mod*a%mod)%mod;
a=(a%mod*a%mod)%mod;
n/=2;
}
return ret;
}
int main(){
LL n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
if(k==1) printf("%lld\n", qsm(qsm(2,n-1),m-1)%mod);
else{
if((n+m)&1) puts("0");
else printf("%lld\n", qsm(qsm(2,n-1),m-1));
}
return 0;
}
C. Marco and GCD Sequence
题意:Marco在梦中遇见一个老头,老头告诉了他长生不老的方法,然后Marco想来后却只记得:给你n个所有GCD的集合S,然后让你去构造原来的序列,使得所有原序列的子区间的GCD都在集合S中。如果不存在则输出-1.
解法:这个题是真的秀了我一脸,一直以为是原序列,结果是every。构造方法十分脑洞:如果最小的GCD集合中的元素不是其他的GCD则输出-1,否则把最小的元素插空到集合中。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, a[maxn];
int main(){
scanf("%d", &n);
for(int i=1; i<=n; i++) scanf("%d", &a[i]);
for(int i=2; i<=n; i++){
if(a[i]%a[1]!=0){
return 0*puts("-1");
}
}
printf("%d\n", n*2);
for(int i=1; i<=n; i++){
printf("%d %d ", a[i], a[1]);
}
printf("\n");
}
D. Ralph And His Tour in Binary Country
题意:给你一个树,边的连接方式是i与i/2连一条无向边,长度为l[i]。q个询问,每个询问给出一个点a和一个值H,求以a为起点到任意点为终点获得(happiness=H-路径长度)不为负数的所有happiness之和。
解法:题解搬运:每一个点递增有序地储存以它为根节点,所有子节点(包括自己)到它的距离并处理出前缀和。所需总空间为O(nlog(n)),然后对于每个询问,从询问点不断往上爬(编号/2就是了)并且减去边长,在每一层时,二分查询左右子树lch和rch(不是刚爬上来的那个子树)中满足条件(总路径小于H)的最大编号,然后就是直接利用前缀和贡献答案就是了。时间复杂度为O(nlog(n) + m(log(n))2)——使用归并排序的情况下(预处理时从下往上处理就可以使用归并了)。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+50;
vector <int> ln[maxn];
vector <LL> sum[maxn];
int n, q, a[maxn], len[maxn];
void Merge(int x, int y){
int ad = len[y];
int i=0,j=0,k=0,lx = ln[x].size(), ly = ln[y].size();
while(i<lx&&j<ly){
if(ln[x][i]<ln[y][j]+ad) a[k++]=ln[x][i++];
else a[k++]=ln[y][j++]+ad;
}
while(i<lx) a[k++]=ln[x][i++];
while(j<ly) a[k++]=ln[y][j++]+ad;
for(i=0;i<lx;i++) ln[x][i]=a[i];
for(i=lx;i<k;i++) ln[x].push_back(a[i]);
}
LL cal(int x, int lx){
int l=0,r=ln[x].size()-1,ans=-1;
while(l<=r){
int mid=(l+r)/2;
if(ln[x][mid]<=lx) ans=mid,l=mid+1;
else r=mid-1;
}
if(ans==-1) return 0;
return 1LL*lx*(ans+1)-sum[x][ans];
}
int main(){
scanf("%d %d", &n,&q);
for(int i=2; i<=n; i++) scanf("%d", &len[i]);
for(int i=1; i<=n; i++) ln[i].push_back(0);
for(int i=n; i>1; i--) Merge(i/2, i);
for(int i=1; i<=n; i++){
for(int j=0,k=ln[i].size();j<k; j++){
sum[i].push_back(ln[i][j]);
if(j) sum[i][j]+=sum[i][j-1];
}
}
while(q--){
int j, k;
scanf("%d %d", &j,&k);
LL now = 0;
for(int last=0;j;k-=len[j],last=j,j>>=1){
if(k<0) break;
now += k;
int lson = j*2, rson = j*2+1;
if(lson<=n&&last!=lson) now += cal(lson, k-len[lson]);
if(rson<=n&&last!=rson) now += cal(rson, k-len[rson]);
}
printf("%lld\n", now);
}
}
E. Ralph and Mushrooms
题意:给你n个点m条边的有向图,每条边都会有蘑菇,一开始第i个边有a[i]个蘑菇,第一次经过能获得a[i]个,第二次能获得a[i]-1个,第三次能获得a[i]-1-2个,直到a[i]非负。现在给你起点,问你最多能获得多少个蘑菇。
解法:非常套路的强连通缩点。一个强连通里面,一定能把所有边的蘑菇都采完。然后缩点之后就是一个dag了,跑个dp就好了。中间算一个边能取多少个蘑菇的时候,用二分就好了。给点编号用unorder_map加速。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e6+7;
vector <int> G[maxn],V[maxn],E[maxn],V2[maxn];
int st, pre[maxn], low[maxn], scc[maxn], dfsclk, scccnt, num[maxn], in[maxn], n, m;
LL val[maxn], dp[maxn];
vector <int> p[maxn], q[maxn];
unordered_map <int, int> mp;
stack <int> S;
void dfs(int u){
pre[u] = low[u] = ++dfsclk;
S.push(u);
for(int i=0; i<G[u].size(); i++){
int v = G[u][i];
if(!pre[v]){
dfs(v);
low[u] = min(low[u], low[v]);
}
else if(!scc[v]){
low[u] = min(low[u], pre[v]);
}
}
if(low[u]==pre[u]){
scccnt++;
do{
u = S.top(); S.pop();
scc[u] = scccnt;
num[scccnt]++;
}while(pre[u]!=low[u]);
}
}
void find_scc(){
dfsclk = scccnt = 0;
memset(pre, 0, sizeof(pre));
memset(scc, 0, sizeof(scc));
for(int i=0; i<n; i++) if(!pre[i]) dfs(i);
}
LL cal_sum(LL x){
x++;
return x*(x-1)*(x+1)/6LL;
}
LL calc(LL x){
LL l=1, r=1e5+7, ans=0;
while(l <= r){
LL mid = (l+r)/2;
if(x>mid*(mid+1)/2){
ans = mid;
l = mid+1;
}else{
r = mid-1;
}
}
return x+x*ans-cal_sum(ans);
}
LL solve(int x){
if(dp[x]!=-1) return dp[x];
dp[x] = 0;
for(int i=0; i<E[x].size(); i++){
dp[x] = max(dp[x], solve(E[x][i])+V2[x][i]);
}
dp[x] += val[x];
return dp[x];
}
int main(){
scanf("%d %d", &n,&m);
for(int i=0; i<m; i++){
int a,b,c;
scanf("%d %d %d", &a,&b,&c);
G[a].push_back(b);
V[a].push_back(c);
}
find_scc();
int tot = 0;
for(int i=1; i<=n; i++){
if(!mp.count(scc[i])){
mp[scc[i]] = tot;
tot++;
}
p[mp[scc[i]]].push_back(i);
}
scanf("%d", &st);
for(int i=0; i<tot; i++){
for(int j=0; j<p[i].size(); j++){
for(int k=0; k<G[p[i][j]].size(); k++){
int u = p[i][j], v = G[u][k];
if(scc[u]==scc[v]){
val[i]+=calc(V[u][k]);
}else{
E[i].push_back(mp[scc[v]]);
V2[i].push_back(V[u][k]);
}
}
}
}
memset(dp, -1, sizeof(dp));
solve(mp[scc[st]]);
LL ans = 0;
for(int i=0; i<tot; i++) ans = max(ans, dp[i]);
return 0*printf("%lld\n", ans);
}