T1 叉叉
题目描述
现在有一个字符串,每个字母出现的次数均为偶数。接下来我们把第一次出现的字母a和第二次出现的a连一条线,第三次出现的和四次出现的字母a连一条线,第五次出现的和六次出现的字母a连一条线...对其他25个字母也做同样3的操作。
现在我们想知道有多少对连线交叉。交叉的定义为一个连线的端点在另外一个连线的内部,另外一个端点在外部。
下图是一个例子,共有三对连线交叉(我们连线的时候,只能从字符串上方经过)。
输入格式
一行一个字符串。保证字符串均由小写字母组成,且每个字母出现次数为偶数次。
输出格式
一个整数,表示答案。
样例输入
abaazooabz
样例输出
3
数据范围
对于30%的数据,字符串长度不超过50。
对于100%的数据,字符串长度不超过100,000。
#include
#include
#include
#include
#define MAXN 100010
using namespace std;
char s[MAXN];
int nxt[MAXN],cnt[26],pos[26],pre[MAXN];
bool is[MAXN];
int main(int argc,char *argv[]) {
freopen("cross.in","r",stdin);
freopen("cross.out","w",stdout);future
scanf("%s",s + 1);
int len = strlen(s + 1);
for(int i=0; i<=25; ++i) pos[i] = len + 1;
memset(nxt,127/3,sizeof nxt );
for(int i=len; i>=1; --i){
++cnt[s[i] - 'a'];
if(!(cnt[s[i] - 'a'] & 1)) is[i] = true;
nxt[i] = pos[s[i] - 'a'],pos[s[i] - 'a'] = i;
}
int Ans = 0;
for(register int i,j=1; j<=26; ++j){
for(i=1; i<=len; ++i)
if(s[i] == 'a' + j - 1) break;
for(int p = i; p<=len; p = nxt[p] ,p = nxt[p])
for(int k=p+1; k<=len && k
nxt[p] && is[k]) ++Ans; } } printf("%d\n",Ans); fclose(stdin); fclose(stdout); return 0; }
题目描述
跳跳虎在外⾯出去玩忘了时间,现在他需要在最短的时间内赶回家。
跳跳虎所在的世界可以抽象成⼀个含有 个点的图(点编号从 到 ),跳跳虎现在在 号点,跳跳虎的家在 号点。
图上⼀共有 条单向边,通过每条边有固定的时间花费。
同时,还存在若⼲个单向传送通道,传送通道也有其时间花费。
传送通道⼀般来说⽐普通的道路更快,但是跳跳虎最多只能使⽤ 次。
跳跳虎想知道他回到家的最⼩时间消耗是多少。
输入格式
第⼀⾏输⼊ 个整数 ( 表⽰点数, 表⽰普通道路的数量, 表⽰传送通道的数量, 表⽰跳跳虎最多使⽤ 次传送通道)
接下来 ⾏每⾏ 个整数 ,表⽰有⼀条从 到 ,时间花费为 的普通道路( )
接下来 ⾏每⾏ 个整数 ,表⽰有⼀条从 到 ,时间花费为 的传送通道( )
输出格式
输出⼀⾏⼀个整数表⽰最⼩时间消耗,如果没法回到家输出 。
样例输⼊
5 5 2 1
1 2 1
1 3 2
2 4 2
3 4 3
4 5 4
1 4 1
2 5 1
样例输出
2
数据范围和约定
对于 的数据,
对于另外 的数据,
对于 的数据,
n 1 n 1 n
m
k
4 n,m,q,k n m q k k
m 3 u,v,w u v w 1 ≤ u,v ≤ n,1 ≤ w ≤ 10 3
q 3 x,y,z x y z 1 ≤ x,y ≤ n,1 ≤ z ≤ 10 3
−1
30% 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,k = 0
30% 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,k = 1
100% 1 ≤ n ≤ 500,0 ≤ m,q ≤ 2000,0 ≤ k ≤ 10^9
#include
#include
#include
#include
#include
#define MAXN 515 #define MAXM 2017 using namespace std; struct Edge{ int to,next,value; }e[MAXM << 2]; int head[MAXN],dis[MAXN],n,m,q,k,tot; bool exist[MAXN]; inline void read(int &x) { x=0; int f=1; register char c=getchar(); while(c>'9'||c<'0') { if(c=='-')f=-1; c=getchar(); } while(c>='0'&&c<='9') { x=x*10+c-'0'; c=getchar(); } x*=f; } inline void Add_Edge(int u,int v,int w) { e[++tot].to = v,e[tot].value = w,e[tot].next = head[u],head[u] = tot; } int SPFA() { memset(dis,-1,sizeof dis ); queue
q; q.push(1); dis[1] = 0; while(!q.empty()) { int u = q.front(); q.pop(); exist[u] = false; for(int i=head[u]; i; i=e[i].next) { int v = e[i].to; if(dis[v] == -1 || dis[v] > dis[u] + e[i].value) { dis[v] = dis[u] + e[i].value; if(!exist[v]) q.push(v),exist[v] = true; } } } return dis[n]; } void solve() { int u,v,w,Ans = 1e9 + 7; for(register int i=1; i<=m; ++i) read(u),read(v),read(w),Add_Edge(u,v,w); ++tot; for(register int i=1; i<=q; ++i) { read(u),read(v),read(w); int temp = head[u]; e[tot].to = v,e[tot].value = w,e[tot].next = head[u],head[u] = tot; int tmp = SPFA(); if(tmp != -1) Ans = min(tmp,Ans); head[u] = temp; } printf("%d\n",Ans); fclose(stdin); fclose(stdout); exit(0); } void xolve() { int u,w,v; for(register int i=1; i<=m; ++i) read(u),read(v),read(w),Add_Edge(u,v,w); printf("%d\n",SPFA()); fclose(stdin); fclose(stdout); exit(0); } void caolidie() { int u,v,w,Ans = 1e9 + 7; for(register int i=1; i<=m; ++i) read(u),read(v),read(w),Add_Edge(u,v,w); for(register int i=1; i<=q; ++i) read(u),read(v),read(w),Add_Edge(u,v,w); printf("%d\n",SPFA()); fclose(stdin); fclose(stdout); exit(0); } int main(int argc,char *argv[]) { freopen("move.in","r",stdin); freopen("move.out","w",stdout); read(n),read(m),read(q),read(k); if(k == 0) xolve(); else if(k == 1) solve(); else caolidie(); return 0; }
今天小a在纸上研究树的形态,众所周知的,有芭蕉树,樟树,函树,平衡树,树套树等等。那么小a今天在研究的就是其中的平衡树啦。
小a认为一棵平衡树的定义为一个n个点,从1到n编号,n-1条边,且任意两点间一定存在唯一一条简单路径,且n>=k。
现在小a看到一棵很大很大的树,足足有n个节点,这里n一定大于等于k!为了方便起见,它想把这个树删去某些边,使得剩下的若干个联通块都满足是平衡树。这时,小b走过来,不屑一顾的说,如果我一条边都不删,那么也算一棵平衡树咯。
小a对于小b的不屑感到很不爽,并问小b,你能算出我删边的方案总数使得满足我的条件吗?两个删边的方案A,B不同当且仅当存在某一条边属于集合A且不属于集合B,或者存在某一条边属于集合B且不属于集合A。为了让你方便,你只要告诉我答案对1000000007(1e9+7)取模就行了。
小b犯了难,找到了身为程序猿的你。
Hint:
样例解释,
第一种方案为不删边,
第二种方案为删去2 3这一条边,
第三种方案为删去3 4这一条边。
分析:我们令dp[i][j]表示以i为根且当前联通块大小为k的方案总数,特别的,dp[i][0]表示割点当前点与其父亲是棵平衡树的方案总数。
对于u的一个孩子v可以得到转移方程dp[u][j+k]=dp[u][j]*dp[v][k]
另外dp[u][0]=Σdp[u][j](j>=题目给定的k)
这样乍看是n^3的,有一个技巧可以做到n^2即每次dp时,只枚举当前u所在子树的大小,每当枚举到它的其中孩子时,当前u所在子树的大小加上它孩子为根的子树的大小。可以理解为每一个点对只被枚举到一次。
最后答案即为dp[root][0]
#include
#include
#include
#include
#define MAXN 2222
#define Mod 1000000007
using namespace std;
typedef long long LL;
int n,K,tot,head[MAXN],siz[MAXN];
LL dp[MAXN][MAXN];
struct Edge { int to,next; }e[MAXN << 1];
inline void Add_Edge(int u,int v) {
e[++tot].to = v,e[tot].next = head[u],head[u] = tot;
e[++tot].to = u,e[tot].next = head[v],head[v] = tot;
}
inline void ADD_MOD(LL &x,LL y) {
x += y;
if(x >= Mod) x -= Mod;
}
inline void read(int &x) {
x = 0; register char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c<= '9') { x = x * 10 + c - '0'; c = getchar(); }
}
void DFS(int u,int fa) {
siz[u] = 1,dp[u][ siz[u] ] = 1;
for(int i=head[u]; i; i=e[i].next) {
int v = e[i].to;
if(v == fa) continue;
DFS(v,u);
for(int j=siz[u]; j>=1; --j){
for(int k=siz[e[i].to]; k>=1; --k)
ADD_MOD(dp[u][j + k],(LL)dp[u][j] * dp[e[i].to][k] % Mod);
dp[u][j] = (LL)dp[u][j] * dp[e[i].to][0] % Mod;
}
siz[u] += siz[e[i].to];
}
for(int k=K; k<=siz[u]; ++k) ADD_MOD(dp[u][0],dp[u][k]);
}
int main(int argc,char *argv[]) {
read(n),read(K);
int u,v;
for(register int i=1; i<=n-1; ++i)
read(u),read(v),Add_Edge(u,v);
DFS(1,-1);
printf("%d\n",dp[1][0]);
return 0;
}